Class Helpers e Record Helpers

Há algumas semanas, enquanto eu examinava a lista de artigos do blog, notei que até o momento não fiz publicações sobre Helpers no Delphi. No artigo de hoje, discorro brevemente sobre o conceito, vantagens, restrições e exemplos deste recurso que considero fantástico!

 

Convenhamos: já passou pela sua cabeça incluir um novo comportamento uma classe da RTL ou VCL do Delphi, não é? Algumas vezes, uma simples operação é tão comum no código que seria muito bom se já estivesse presente na classe nativa. Pois bem, pessoal, os Helpers existem para isso!

 

Afinal, o que são Helpers?

Em poucas palavras, trata-se de uma estrutura que introduz comportamentos ou atributos adicionais à uma classe ou record sem a necessidade de utilizar heranças. Com Helpers, portanto, podemos inserir novas funcionalidades de maneira fácil e usá-las como se fizessem parte da classe que estendemos.
Helpers são divididos em dois tipos: Class Helpers e Record Helpers. O primeiro, como o próprio nome diz, é associado à classes. O segundo permite a extensão de tipos de dados, como string, integer e enumerados.
A sintaxe de ambos segue basicamente o seguinte padrão:

{Nome do Helper} = {Tipo} helper for {Nome do Tipo}
  { Métodos ou atributos desejados } 
end;

 

Exemplo de Class Helper

Considere, por exemplo, que seja necessário verificar se o conteúdo de um componente TEdit está vazio. Provavelmente escreveríamos o código a seguir:

result := Trim(Edit1.Text) = EmptyStr;

Imagine se fosse possível executar a mesma validação apenas com a instrução abaixo:

result := Edit1.IsEmpty;

Bem melhor, não? O código ficaria mais limpo e expressivo.

Porém, sabemos que TEdit é um componente nativo da VCL do Delphi e não podemos simplesmente alterá-lo. Uma alternativa seria criar um componente herdado de TEdit para adicionar essa funcionalidade, mas, para isso, teríamos que criar um pacote para instalá-lo, e depois substituir todos os componentes TEdit na aplicação por este novo componente. Em suma: daria um trabalhão!
Com Class Helpers, podemos adicionar essa funcionalidade sem a necessidade de alterar a definição da classe TEdit. Ao invés disso, criaremos uma “extensão” da classe:

type
  TEditHelper = class helper for TEdit
    function IsEmpty: boolean;
  end;
 
{ TEditHelper }
 
function TEditHelper.IsEmpty: boolean;
begin
  result := Trim(Self.Text) = EmptyStr;
end;

Só isso, pessoal! O próximo passo é declarar a unit que contém esse Helper na seção uses do formulário no qual desejamos utilizá-lo.
Observem, em seguida, que o próprio Code Completion do Delphi já sugere a nossa função, como se ela existisse na própria classe TEdit:

Exemplo de Class Helper para o componente TEdit

Muito bom, hein? 🙂
Vale lembrar que Class Helpers também podem ser aplicados à RTL. Há algumas semanas, por exemplo, criei um pequeno Helper para a classe TStringList para retornar a existência de um valor específico contido na lista.

 

Exemplo de Record Helper

Os Record Helpers, por sua vez, nos dão a possibilidade de incluir novos comportamentos a tipos de dados. Como exemplo, suponha que a formatação de um número para valor monetário (com cifrão) seja algo bastante comum no software. Ao invés de utilizarmos o FormatFloat em todas as ocorrências no código, podemos criar um Record Helper para o tipo double com a responsabilidade de retornar o valor formatado:

type
  TDoubleHelper = record helper for double
    function ValorMonetario: string;
  end;
 
{ TDoubleHelper }
 
function TDoubleHelper.ValorMonetario: string;
begin
  result := FormatFloat('R$ ###,##0.00', Self);
end;

Dessa forma, ao escrever este código…

var
  Valor: double;
begin
  Valor := 1435.89;
  ShowMessage(Valor.ValorMonetario);
end;

… recebemos o seguinte resultado:

Exemplo de Record Helper para o tipo double

 

Use com moderação!

O propósito principal dos Helpers é permitir a extensão de estruturas que não podem ser herdadas ou modificadas. Isso significa que, para classes já criadas no projeto, recomenda-se a alteração de sua própria estrutura ao invés da criação de Helpers. Caso contrário, com o passar do tempo, a existência de vários Helpers no projeto pode dificultar a identificação de comportamentos e, por consequência, a manutenção do projeto.

 

Restrições

Em julho, recebi um comentário interessante de um leitor chamado Eduardo sobre uma restrição de Record Helpers que até então eu desconhecia. Ao referenciar duas units que contém Record Helpers para o mesmo tipo, apenas a última unit é considerada. Por exemplo, considere as referências abaixo:

uses
  HelperString1, HelperString2;

A unit “HelperString1” será ignorada pelo Delphi e apenas a unit “HelperString2” entrará em vigor.
Após algumas pesquisas, encontrei uma explicação da própria Embarcadero sobre essa restrição:

Você pode definir e associar múltiplos Helpers para um único tipo. Porém, apenas um deles terá vigência em qualquer local específico do código. O Helper referenciado no escopo mais próximo (o primeiro da direita para a esquerda na seção uses) será aplicado.

Portanto, procure declarar todas as funcionalidades adicionais de uma classe ou record em uma única unit para cada tipo. Ou então, organize as units de tal forma que apenas uma seja referenciada na seção uses de uma classe.

 

Grande abraço, pessoal!


 

Comentários

  1. Jocimar Huss

    09/08/2018 - 23:26

    Parabéns. Mais um artigo bem sucinto e rico!

  2. Cristiano Nunes

    11/08/2018 - 10:46

    Olá pessoal, me chamo Cristiano e preciso de uma ajuda. Estou com uns bugs no meu sistema e precisaria de um profissional em Delphi 5 meu contato.

  3. Oi, André.

    Eu não testei aqui, mas acho que essa limitação pode ser contornada fazendo um helper do helper, entendeu?
    Não estou aqui pra dizer se é certo, se viola algum padrão etc, mas um helper que descende de outro helper não causa problemas, entãoo no caso acima, não precisaria colocar HelperString1, HelperString2.
    Bastaria
    HelperString2

    Pq ele já seria um helper de HelperString1

    Abs

    • André Celestino

      13/08/2018 - 23:18

      Boa noite, Conde! Sumido! 🙂
      Você tem razão quanto ao conceito, porém, infelizmente não é possível implementar um Helper de um Helper, como também não é possível utilizar heranças. Por esse motivo que essa restrição existe e já foi explanada na documentação da Embarcadero.
      Abraço!

  4. ue andre, ou o diego garcia esta errado ou eu n entendi o artigo, kkkkk

    veja
    https://drgarcia1986.wordpress.com/2013/09/19/use-class_record-helpers-no-delphi/

    la no fim:

    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;

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *