Introdução: O Fim da uFuncoesUteis.pas
Todo desenvolvedor Delphi já encontrou (ou criou) a infame unit uFuncoes.pas ou uUtils.pas. É aquele arquivo gigantesco, repleto de procedimentos globais e funções soltas, que serve como um “canivete suíço” para o projeto.
Embora funcional, essa abordagem tem problemas arquiteturais:
- Baixa Coesão: Mistura validação de documentos, formatação de datas e manipulação de arquivos no mesmo lugar.
- Programação Procedural: Obriga você a escrever
ValidaCNPJ(Cliente.CNPJ)em vez de uma abordagem orientada a objetos fluida. - Poluição do Namespace: Inúmeras funções globais disponíveis o tempo todo.
Desde o Delphi 2005 (para .NET) e posteriormente para Win32, a Embarcadero introduziu os Helpers (Class Helpers e Record Helpers). Este recurso permite estender classes e tipos existentes sem a necessidade de herança, permitindo que você escreva códigos como Cliente.CNPJ.IsCNPJ ou Query.ToJSON.
Neste artigo, vamos explorar a fundo como utilizar Helpers para limpar seu código, analisando exemplos práticos, limitações técnicas e as melhores práticas.
O Que São Helpers?
Tecnicamente, um Helper é uma construção do compilador que permite adicionar métodos (procedures e functions) e propriedades a uma classe ou record (struct) existente.
Eles funcionam como “syntactic sugar” (açúcar sintático). Quando você chama MeuObjeto.MetodoDoHelper, o compilador traduz isso internamente para uma chamada estática passando a instância do objeto como parâmetro.
Sintaxe Básica
type
TNomeDoHelper = class helper for TClasseAlvo
public
procedure NovoMetodo;
end;
Cenário 1: Limpando Validações com Record Helpers
No Delphi moderno, tipos nativos como string, Integer e TDateTime são tratados como Records em muitos contextos, permitindo o uso de Record Helpers.
Vamos transformar a validação de documentos em uma extensão natural do tipo string.
Atenção: O Delphi já possui um TStringHelper nativo em System.SysUtils. Ao declarar um helper para string, você pode ocultar o helper padrão (veja a seção de Limitações).
Implementação: TStringValidationHelper
unit Lib.Helpers.Strings;
interface
uses
System.SysUtils, System.RegularExpressions;
type
// Usamos 'record helper' pois string é um tipo intrínseco/record
TStringValidationHelper = record helper for string
public
function OnlyNumbers: string;
function IsCNPJ: Boolean;
function IsCPF: Boolean;
end;
implementation
{ TStringValidationHelper }
function TStringValidationHelper.OnlyNumbers: string;
begin
// Remove tudo que não for dígito (0-9)
Result := TRegEx.Replace(Self, '[^0-9]', '');
end;
function TStringValidationHelper.IsCNPJ: Boolean;
var
LInput: string;
begin
LInput := Self.OnlyNumbers; // Chamando outro método do helper
if LInput.Length <> 14 then
Exit(False);
// Lógica simplificada de validação (apenas para exemplo)
// Adicione aqui o algoritmo de módulo 11 real
Result := True;
end;
function TStringValidationHelper.IsCPF: Boolean;
begin
Result := Self.OnlyNumbers.Length = 11;
// Implementar algoritmo de CPF
end;
end.
Como usar:
uses
Lib.Helpers.Strings;
procedure TForm1.btnValidarClick(Sender: TObject);
var
CNPJ: string;
begin
CNPJ := '12.345.678/0001-99';
// Leitura fluida e limpa
if CNPJ.IsCNPJ then
ShowMessage('Válido!')
else
ShowMessage('Inválido: ' + CNPJ.OnlyNumbers);
end;
Cenário 2: Turbinando Componentes com Class Helpers
Imagine exportar o conteúdo de um TDataSet (FireDAC, dbExpress, ou ClientDataSet) para JSON sem criar classes intermediárias ou usar componentes visuais pesados.
Implementação: TDataSetHelper
unit Lib.Helpers.DataSet;
interface
uses
Data.DB, System.JSON, System.SysUtils;
type
TDataSetHelper = class helper for TDataSet
public
function ToJSON: TJSONArray;
end;
implementation
{ TDataSetHelper }
function TDataSetHelper.ToJSON: TJSONArray;
var
LRow: TJSONObject;
LField: TField;
LBookMark: TBookmark;
begin
Result := TJSONArray.Create;
// Evita mover o cursor do dataset original permanentemente
if Self.IsEmpty then Exit;
Self.DisableControls;
LBookMark := Self.GetBookmark;
try
Self.First;
while not Self.Eof do
begin
LRow := TJSONObject.Create;
for LField in Self.Fields do
begin
// Exemplo simplificado. Em produção, tratar tipos (Date, Blob, etc)
case LField.DataType of
ftInteger, ftLargeint:
LRow.AddPair(LField.FieldName, TJSONNumber.Create(LField.AsLargeInt));
ftFloat, ftCurrency:
LRow.AddPair(LField.FieldName, TJSONNumber.Create(LField.AsFloat));
else
LRow.AddPair(LField.FieldName, LField.AsString);
end;
end;
Result.AddElement(LRow);
Self.Next;
end;
// Restaura posição original
if Self.BookmarkValid(LBookMark) then
Self.GotoBookmark(LBookMark);
finally
Self.FreeBookmark(LBookMark);
Self.EnableControls;
end;
end;
end.
Como usar:
uses
Lib.Helpers.DataSet;
procedure TForm1.ExportarDados;
var
LArray: TJSONArray;
begin
// QueryPadrao é um TFDQuery ou TClientDataSet
LArray := QueryPadrao.ToJSON;
try
Memo1.Lines.Text := LArray.ToString;
finally
LArray.Free;
end;
end;
Detalhes Técnicos Críticos
Embora poderosos, os Helpers possuem regras estritas de escopo e limitações que todo arquiteto de software deve conhecer.
1. A Regra de Visibilidade (O “Último Vence”)
Esta é a regra mais importante: Apenas um Helper pode estar ativo para um tipo em determinado escopo.
Se você tem duas units, UnitA (com THelperA para TEdit) e UnitB (com THelperB para TEdit), e faz isso:
uses UnitA, UnitB;
Apenas os métodos de THelperB estarão visíveis para TEdit. O compilador não mescla os helpers. O helper definido na unit mais à direita da cláusula uses tem prioridade.
Dica de Ouro: Para tipos nativos como string, criar um helper próprio pode esconder os métodos padrão do Delphi (como ToString, Split, Join que residem em System.SysUtils.TStringHelper).
2. Sem Estado (No State)
Helpers não podem adicionar novos campos (variáveis de instância) à classe ou record.
- ❌
FCount: Integer;(Proibido) - ✅
class var FCount: Integer;(Permitido, mas é estático/global para o tipo) - ✅
property Count: Integer read GetCount;(Permitido se usar métodos existentes)
3. Acesso a Private/Protected
Curiosamente, class helpers podem acessar membros private e protected da classe que estão estendendo (desde que estejam na mesma unit ou em versões do Delphi que permitam o “backdoor”). Observação: Embora útil para corrigir bugs em componentes de terceiros sem código fonte, usar helpers para violar encapsulamento deve ser evitado em design de código limpo.
4. Herança de Helpers
Um helper pode herdar de outro helper. Isso é útil para estender o TStringHelper padrão sem perder suas funcionalidades originais (dependendo da versão do Delphi, a herança de record helpers tem comportamentos específicos).
type
TMeuHelper = class helper(TParentHelper) for TMinhaClasse
...
end;
Prós e Contras
✅ Vantagens
- Intellisense (Code Completion): Seus métodos aparecem ao digitar o ponto (
.), facilitando a descoberta de funcionalidades por novos desenvolvedores. - Legibilidade:
Data.ToISO8601é mais legível queDateToISO8601(Data). - Extensibilidade: Permite adicionar funcionalidades a componentes da VCL/FMX ou bibliotecas de terceiros sem criar subclasses (ex:
TEditEx).
❌ Desvantagens
- Ambiguidade de Escopo: O desenvolvedor pode não saber de qual unit vem aquele método
ToJSONse houver múltiplos helpers. - Ocultação acidental: Risco de esconder métodos padrão da linguagem se não gerenciar a cláusula
usescorretamente. - Acoplamento Oculto: O código parece pertencer à classe, mas depende de uma unit externa.
Conclusão
Helpers são uma ferramenta formidável para modernizar bases de código Delphi. Eles permitem migrar de uma arquitetura procedural (baseada em units utilitárias globais) para uma abordagem mais orientada a objetos e fluida.
Ao substituir uFuncoes.pas por TStringHelper, TDataSetHelper ou TFileHelper, você não apenas organiza seu projeto, mas torna a escrita do código mais intuitiva. Contudo, use com sabedoria: mantenha o escopo controlado e evite criar “God Helpers” que fazem tudo.
Referências Validadas
- Embarcadero DocWiki – Class and Record Helpers: http://docwiki.embarcadero.com/RADStudio/Sydney/en/Class_and_Record_Helpers
- Object Pascal Handbook (Marco Cantù): Consulte a seção sobre Type Helpers para detalhes sobre a evolução do recurso desde o Delphi 2007.
- Clean Code no Delphi: A utilização de helpers alinha-se aos princípios de “Extension Methods” encontrados em outras linguagens modernas como C# e Kotlin.
Descubra mais sobre Régys Borges da Silveira
Assine para receber nossas notícias mais recentes por e-mail.
Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.