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:
1 2 3 |
{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:
1 |
result := Trim(Edit1.Text) = EmptyStr; |
Imagine se fosse possível executar a mesma validação apenas com a instrução abaixo:
1 |
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:
1 2 3 4 5 6 7 8 9 10 11 |
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
:
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:
1 2 3 4 5 6 7 8 9 10 11 |
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…
1 2 3 4 5 6 |
var Valor: double; begin Valor := 1435.89; ShowMessage(Valor.ValorMonetario); end; |
… recebemos o seguinte resultado:
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:
1 2 |
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!
Parabéns. Mais um artigo bem sucinto e rico!
Obrigado, grande Jocimar!
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.
Vou entrar em contato com você, Cristiano.
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
Boa noite, Conde! Sumido! 🙂
Você tem razão quanto ao conceito, porém, infelizmente não é possível implementar um Helper de um Helper. Por esse motivo que essa restrição existe e já foi explanada na documentação da Embarcadero.
Abraço!
André, ou o Diego Garcia está errado ou eu não entendi o artigo, kkkkk
Veja:
https://drgarcia1986.wordpress.com/2013/09/19/use-class_record-helpers-no-delphi/
Lá 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:
Isso mesmo, Conde. O Diego Garcia é sensacional!
Porém, observe que ele codifica essa herança para Class Helpers. A restrição que mencionei no artigo envolve apenas Record Helpers. Infelizmente não é possível, por exemplo, fazer dessa forma:
Abraço!
Oi, André.
Acho que agora nos entendemos então, kkkkkkk.
Helper de Helper é possivel para classe mas não para record.
Interessante.
Olá, André. Estou usando Helper para trazer o SQL com os parâmetros da Query preenchido e funciona perfeitamente, entretanto, quando eu utilizo no watch (Ctrl+F5 ou Ctrl+F7) para visualizar o conteúdo enquanto estou debugando, não é possível visualizar o conteúdo. Sabe dizer por que isso acontece, se tem solução, ou se tem que ser implementado de algum modo específico?
Segue código:
Abraço.
Olá, Gilmar, tudo bem?
Fiquei curioso com o seu caso. Vou entrar em contato.
Abraço!
Olá André . acho que digitei errado meu email correto era “gmail”
Tem alguma novidade sobre esse caso quando usa em debug?
Abraço.
Agora enviei para o e-mail correto, Gilmar!
Muito util. Parabéns pela iniciativa de divulgar.
Obrigado, Abel!
Parabéns André! Ótimo artigo para enriquecer o conhecimento.
Obrigado, Luan! 🙂
Boa tarde André,
Me deparei com esse problema recentemente, fui fazer um helper para string para ignorar o CaseSensitive somente no comando, e vi que os helper herdados de SysUtils foram sobrescritos… tentei fazer um helper do helper, mas sem sucesso também…
Resolvi o meu problema de record helper criando um novo tipo de string.
Quando quero utilizar os novos helper da minha classe ou faço um cast na string ou já declaro a mesma com o novo tipo.
Sei que não é a melhor solução mais resolveu pra mim.
Um ótimo artigo, esclareceu bem.
Abraço.
Olá, Jonathan.
Pois é, infelizmente não existe uma forma de usar dois (ou mais) record helpers para o mesmo tipo. O compilador considera apenas o último declarado.
A forma que você encontrou para contornar isso é bastante válida, embora não seja a forma como gostaríamos de resolver, rsrs.
De qualquer forma, enquanto não surge uma correção para esse problema, continue usando o novo tipo de string declarado.
Obrigado pelo comentário!
Abraço!
Ola, André excelente matéria. Eu uso o Delphi Xe2 e o helper Integer não funciona, pois nesta versão ainda não é era um tipo primitivo, tem algum ideia ou sugestão? Grande abraço.
Olá, Ronaldo!
Infelizmente os Record Helpers foram introduzidos a partir da versão XE3 do Delphi. Como no XE2 ainda não está disponível, você pode criar uma classe estática que “simula” um Helper:
O problema é que a chamada ficaria bem diferente de um Helper:
No entanto, acredito que essa seria a única solução 🙁
Abraço!
Boa noite e parabéns pelo belíssimo artigo.
Uma dúvida posso usar o class helper para modificar métodos já existentes na classe?
Olá, José!
Rapaz, não sei o que houve, mas o blog não me notificou do seu comentário. Desculpe pelo atraso.
Sim, José, se você declarar um método no Helper que tenha o mesmo nome de um método já existente no componente, ocorre uma sobrescrita.
Vale frisar que, neste caso, você “perde” acesso ao método original, ou seja, não é como uma herança, na qual você pode invocar o método original com inherited.
Abraço!
Olá. Todos estão bem? Experimente fazer assim:
V := Unidade1.Tipo;
Objeto := Unidade1.TObjeto.Create;