Você sabe o que vem a ser um Class Helper?
Class Helper é uma técnica da programação orientada a objetos que tem o intuito de estender as funcionalidades de uma classe, sem que seja necessário utilizar herança e sem que seja necessário alterar a classe original.
Imagine o seguinte cenário. Em sua aplicação existe a classe TPessoa, que por algum motivo você não pode alterar a sua implementação, mas, necessita de um determinado método nesta classe, como por exemplo retornar a idade da pessoa a partir de sua data de nascimento.
Em situações normais, seria feito um método CalcularIdade() por exemplo e seria usado da seguinte forma:
{...} EdtIdade.Text := IntToStr(CalcularIdade(oPessoa.DataNasc)); {...}
Agora imagine se fosse desta forma:
{...} EdtIdade.Text := oPessoa.Idade.ToString; {...}
Muito mais claro e limpo. Mas como fazer isso sem ter acesso a implementação da classe TPessoa? Simples, fazendo um Class Helper para a classe TPessoa.
Faremos um exemplo bem simples para mostrar o uso prático do recurso. Vamos começar pela interface da classe TPessoa:
type TPessoa = class private FDataNasc: TDate; public property DataNasc : TDate read FDataNasc write FDataNasc; end;
Usaremos somente o campo referente a data de nascimento, pois ele está no foco do nosso exemplo. Agora faremos o nosso Class Helper para a classe TPessoa:
type TPessoaHelper = class helper for TPessoa private function GetIdade: integer; public property Idade : integer read GetIdade; end; implementation uses DateUtils, System.SysUtils; { TPessoaHelper } function TPessoaHelper.GetIdade: integer; begin Result := YearsBetween(Self.DataNasc,Now); end;
Note que para determinar que uma classe é um class helper de outra é necessário utilizar a diretiva helper e indicar a classe principal com a palavra reservada for.
Outro ponto que merece atenção é o fato de que, na classe helper, nós temos acesso a todos os atributos públicos da classe principal a partir da própria instância do helper, por esse motivo que foi possível acessar a propriedade DataNasc.
Pronto, agora a propriedade Idade já está disponível para todos os objetos do tipo TPessoa em units que fizerem referência a unit do class helper (caso o class helper TPessoaHelper tenha sido feito em uma unit diferente da unit da classe TPessoa).
Por exemplo:
uses unt_pessoa, unt_pessoa_helper; procedure TForm1.Button1Click(Sender: TObject); var oPessoa : TPessoa; begin oPessoa := TPessoa.Create; try oPessoa.DataNasc := StrToDate('26/11/1986'); EdtIdade.Text := IntToStr(oPessoa.Idade); finally oPessoa.Free; end; end;
Mas nosso exemplo ainda não terminou. Note que ainda estou fazendo um type casting explícito na idade, como resolver isso? Utilizando Record Helper :). A partir do Delphi XE3, é possível criar helpers para tipos primitivos, como por exemplo o tipo integer.
Faremos um record helper para o tipo integer com o simples intuito de converter o inteiro para uma string:
TIntHelper = record helper for integer public function ToString():String; end; implementation function TIntHelper.ToString: String; begin Result := IntToStr(Self); end;
Agora sim, nosso exemplo está completo:
procedure TForm1.Button1Click(Sender: TObject); var oPessoa : TPessoa; begin oPessoa := TPessoa.Create; try oPessoa.DataNasc := StrToDate('26/11/1986'); EdtIdade.Text := oPessoa.Idade.ToString; finally oPessoa.Free; end; end;
Vale lembrar que utilizando o helper TIntHelper que criamos, abrimos margem para substituir a clássica conversão StrToInt para simplesmente utilizar a nossa função ToString():
var iNumero : integer; sTexto : string; begin sTexto := 1.ToString; //sTexto = '1' iNumero := 2; sTexto := iNumero.ToString; //sTexto = '2' end;
Mas antes de sair criando helpers para tipos conhecidos, verifique se o próprio Delphi já não disponibiliza um helper para o mesmo intuito. A unit SysUtils possui uma série de helpers para os tipos mais comumente utilizados.
Uma dúvida que sempre aparece quando se começa a utilizar class helpers é com relação a criação de mais de um helper para uma determinada classe. Por exemplo, o seguinte código não funciona da maneira que intuitivamente se espera:
TPessoaHelper = class helper for TPessoa private function GetIdade: integer; public property Idade : integer read GetIdade; end; TPessoaFisicaHelper = class helper for TPessoa //esse passa a ser o Helper "ativo" public function isValidCPF():Boolean; end;
Não ocorre um erro de compilação, porém, a propriedade Idade, não estará mais disponível, pois um helper se sobrepõe ao outro. Para que isso não ocorra, é necessário indicar o class helper antecessor, desta forma:
TPessoaHelper = class helper for TPessoa private function GetIdade: integer; public property Idade : integer read GetIdade; end; TPessoaFisicaHelper = class helper (TPessoaHelper) for TPessoa public function isValidCPF():Boolean; end; TPessoaJuridicaHelper = class helper (TPessoaFisicaHelper) for TPessoa public function isValidCNPJ():Boolean; end;
Devido a evoluções que a arquitetura do Delphi vem sofrendo, principalmente pelas inovações relacionadas a mobilidade, a própria Embarcadero recomenda que se use classe/record helpers para determinados fins, fazendo com que o seu código esteja protegido de eventuais mudanças mais trágicas, como por exemplo a promessa de extinção da concatenação de strings através do simbolo de adição (Ex: Str1 := ‘Texto ‘ + IntToStr(3));
Acabaram com o + no Dart depois voltaram atrás.
Todo mundo sabe a merda que pode fazer usando ele dentro de um LOOP,mas só faz quem
quer.
Lembrando que o Delphi conviveu durante várias versões com um Bug relacionado a utilziação de Class Helpers,que só foi solucionado agora no Berlin.
http://blog.marcocantu.com/blog/2016-june-closing-class-helpers-loophole.html