O uso de Class Helpers em Delphi pode reduzir o tempo de desenvolvimento em até 30%

Última atualização em 31 de agosto de 2022 por Willian Tuttoilmondo

Como aumentar sua produtividade com Class Helpers

Um dos grandes avanços promovidos pelo paradigma de desenvolvimento orientado a objetos, principalmente nos últimos anos, foi a criação de auxiliares que podem ser agregados às classes para efetuar um conjunto específico de ações. A esses auxiliares foi dado um nome um tanto óbvio, mas certeiro: Class Helpers. Estes elementos foram criados para que desenvolvedores pudessem adicionar funcionalidades a uma classe sem precisar estendê-la em uma classe derivada. Isso é extremamente importante, principalmente quando temos desenvolvimentos legados que utilizam, por exemplo, generics e não podemos mudar o tipo base utilizado na implementação mas, mesmo assim, temos que adicionar funcionalidades à uma classe.

Class Helpers não são, extamente, uma novidade. Em outras linguagens, e até mesmo em Delphi, eles já estão por aí há algum tempo mas, como quase ninguém fala deles, muita gente ainda não os conhece. O que veremos aqui é como implementar um Class Helper de maneira a facilitar desenvolvimentos futuros e, assim, ganhar em produtividade e eficiência.

Ah, ao final do artigo você encontrará um link para um pequeno conjunto de Class Helpers desenvolvidos e exibidos aqui.

Antes de começar, vamos às regras

Assim como qualquer implementação, os Class Helpers têm regras a serem seguidas. Não é nada muito extraordinário mas, mesmo assim, elas servem para auxiliar o desenvolvedor a compreender e usar melhor este recurso. Em Delphi, como não poderia deixar de ser, também precisamos nos ater a elas. Sendo assim, aqui estão:

  • Class Helpers não podem conter construtores ou destrutores;
  • Class Helpers não podem conter campos;
  • Class Helpers não podem conter métodos abstratos;
  • Class Helpers não podem sobrescrever métodos da classe estendida.

Entendendo estas quatro regras muito simples, precisamos ter em mente que, ao criarmos um Class Helper, este não pode ser referenciado em nenhuma parte do código fonte, a não ser nos seguintes casos:

  • Quando declarando a extensão de um Class Helper;
  • Ao ser usado em uma chamada (Bit)SizeOf;
  • Ao ser usado em uma chamada TypeInfo.

Class Helpers - Devspace

Outra característica à qual precisamos nos ater é o fato de que, independente do número de Class Helpers que tenhamos desenvolvido para uma classe, apenas um pode ser usado por vez. Mesmo que tenhamos criado vários Class Helpers, e declarado todos eles na seção uses, apenas um deles será efetivamente utilizado.

Como isso funciona na prática?

Bem, vamos começar pelo básico. Para usar um Class Helper, antes de tudo, precisamos ter uma classe para efetuar tais “personalizações”. Podemos usar uma classe já existente (como a TStrings, por exemplo) ou criar uma classe novinha em folha. No intuito de manter o artigo o mais didático possível, vamos criar uma classe e, depois, criar um Class Helper para ela. Sendo assim, vamos lá.

E, com a classe definida, vamos definir nosso Class Helper.

Agora que já temos uma classe e um Class Helper criados, podemos ver como uní-los para que tudo funcione.

Aqui podemos ver alguns exemplos de orientação a objetos, alguns que já discutimos antes, como Singleton, por exemplo. Mas, mais importante que isso, podemos perceber que, ao unir uma classe a um Class Helper, podemos facilitar – e muito – o desenvolvimento de funcionalidades sem a necessidade de criar extensões de classes para que elas executem métodos diferentes dos que foram originalmente projetados para ela. Originalmente, a classe possui apenas o método Response mas, ao adicionarmos o Class Helper, o objeto derivado da classe TNewClass também pode executar o método NewResponse. Fácil, não?!

Class Helpers e a ocultação de métodos

Uma característica do uso de Class Helpers é a ocultação de métodos. Lá nas regras eu deixei bem claro que não podemos sobrescrever métodos, mas não disse que não poderíamos ocultá-los. Mas, como assim?!

Vamos supor que tenhamos uma necessidade “obscura” de alterar completamente o processamento em um método qualquer de uma classe. Em uma extensão, criaríamos um método com sobrescrita (override) – desde que removamos a instrução de uso da parte herdada do método (inherited) – ou reintrodução (reintroduce) do método original. Num Class Helper, claro, a coisa é diferente (e mais simples). Vejamos o código de um novo Class Helper para nossa classe TNewClass.

Sendo assim, ao declararmos o uso do novo Class Helper, o compilador entenderá que o método Response a ser utilizado é o que faz parte do Class Helper e não o original da classe, ou seja, a resposta será 42 (constante no Class Helper) ao invés de 33 (constante na classe).

Ah, e claro que também temos uma regra para isso: o método no Class Helper deve ter a mesma assinatura que o método da classe, ou isso gerará um erro em tempo de compilação. Simples!

E se eu quiser desenvolver mais de um Class Helper para a minha classe?

Você pode, sem problema algum. Lembra que lá nas regras eu disse que “independente do número de Class Helpers que tenhamos desenvolvido para uma classe, apenas um pode ser usado por vez”? Isso significa que, mesmo que eu declare o uso de mais de um Class Helper para minha classe, o compilador entenderá que apenas o último deve ser considerado e eliminará os demais em tempo de compilação.

Vamos desenvolver um terceiro Class Helper para exemplificar como isso funciona.

Vamos agora adicionar ao nosso Singleton este novo Class Helper.

Como disse antes, o compilador Delphi entende que apenas um Class Helper pode ser vinculado à classe a que ele se destina. Sendo assim, apenas o último Class Helper declarado é considerado pelo compilador, sendo os demais descartados. Mas, como somos teimosos e decidimos utilizar um método constante no primeiro Class Helper, isso gerará um erro de compilação, já que o compilador ignora o primeiro Class Helper e, portanto, seu método não pode ser encontrado.

Mas tem alguma utilidade prática pra tudo isso?

Claro! Uma das principais formas de tornar um código Delphi mais limpo, eficiente e produtivo é a utilização de Class Helpers para diminuir o número de instruções que o desenvolvedor necessita para chegar a um determinado resultado. Um dos meus favoritos é um Class Helper que desenvolvi para minhas APIs desenvolvidas em Delphi. Para trabalhar com objetos JSON de maneira mais prática, acabei desenvolvendo Class Helpers que tornam mais simples e intuitiva a sintaxe na hora de decompor e analisar, em tempo de excução, os objetos trocados na comunicação.

Abaixo, seguem os códigos de dois destes Class Helpers:

Esses Class Helpers facilitam muito meu código e aumentam muito minha produtividade. Um exemplo é este método que tenho em uma classe que converte um registro do banco de dados em um objeto JSON:

E existem Class Helpers para tipos primitivos?

Sim, existem, mas eles não se chamam Class Helpers, mas Record Helpers. Em essência, eles são idênticos aos Class Helpers, mas a diferença está no fato que, ao invés de classes, eles estendem tipos como string, integer e extended, por exemplo. Vocês devem ter notado que eu tenho declarado nos meus Class Helpers de para JSON um Record Helper chamado Helper.Str. Como o nome sugere, ele é um helper desenvolvido para simplificar a sintaxe de operações com strings. Mesmo que a própria Embarcadero já tenha desenvolvido um e que eu pudesse utilizá-lo (ele está na unit System.SysUtils), a sintaxe de alguns métodos nunca me agradou. Então, por uma questão pessoal, acabei desenvolvendo este Record Helper e ele se prova muito útil, principalmente quando preciso executar operações encadeadas com strings. Um exemplo pode ser visto no seguinte trecho de código do Class Helper para a classe TJSONValue:

A linha Self.ToString.Trim.Unquote mostra que, além do método interno do Class Helper ToString, que converte o TJSONValue em string, os métodos Trim e Unquote, presentes no Record Helper para tratar strings, são invocados em uma única linha, já que o retorno de cada um deles é a própria string.

E para não deixar ninguém com a sensação de que a informação está incompleta, aqui segue meu Record Helper para o tratamento de strings:

E eu posso começar a usar isso já?

Eu costumo dizer que não existem restrições (além das óbvias restrições de tecnologia) para o que você pode fazer em termos de desenvolvimento, a não ser a sua própria limitação. Utilizar Class Helpers é uma opção, mas que traz consigo varias vantagens. Aumentar a produtividade, simplificar o código e automatizar ações são algumas destas vantagens. Contudo, há um preço a se pagar…

O sucesso – ou o fracasso – do uso de Class Helpers vai depender diretamente do nível de maturidade dos desenvolvedores envolvidos neste processo. Desenvolvedores menos experientes tendem a desconhecer, ignorar ou rejeitar seu uso. Programadores mais experientes, os quais já “aprenderam” a fazer as coisas de outra maneira ou vêm de versões do IDE que não suportam esta funcionalidade terão muita dificuldade em assimilar e usar Class Helpers. Minha dica é: use sempre que puder, mas tenha em mente que, dependendo da situação, isso pode trazer mais transtornos que vantagens.

E, apesar de que muitos autores e desenvolvedores digam que não é prudente utilizar Class Helpers para modificar o comportamento de classes que podem ser estendidas, imaginem, em um cenário em que o número de desenvolvedores é realmente numeroso e a aplicação já sofre com o tamanho final do arquivo compilado. Neste cenário, se cada desenvolvedor estender uma classe para sua utilização – e vale lembrar que, em quase todos os casos de sobrescrita, esta é feita através da diretiva virtual, a qual, ao contrário da diretiva dynamic, gera uma cópia do método sobrescrito em cada extensão para facilitar o processamento em detrimento do tamanho final do arquivo compilado -, faremos com que o executável fique ainda mais volumoso, o que dificulta processos de atualização pela internet, sem contar a própria memória consumida pela aplicação. Mas, enfim, essa é uma discussão para outro momento.

Espero que este artigo possa ajudar a elucidar o mistério em torno dos Class Helpers e sua utilização e que ele possa, como coloquei lá no título, aumentar sua produtividade e tornar seu código mais eficiente.

E aqui você pode fazer o download de um pequeno conjunto de Class Helpers muito úteis e demonstrados aqui.

6 Comentários

Class Helpers: aumentando a produtividade do desenvolvimento em Delphi

  1. Boa tarde, atualmente uso XE2. Quando compilo o código abaixo retorna o seguinte erro:”[DCC Error] UEnumerator.pas(15): E2474 Record type required”.

    type
    TDias = (Domingo, Segunda, Terca, Quarta, Quinta, Sexta, Sabado);

    TDiasHelper = record helper for TDias
    function GerValue : String;
    end;

    1. Boa tarde.

      Fiz um teste com o Delphi Tokyo (não tenho mais o XE2) e o Código abaixo funcionou perfeitamente. O seu problema pode ser relacionado à versão do Delphi.

      unit Unit1;

      interface

      uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

      type
      TForm1 = class(TForm)
      private
      { Private declarations }
      public
      { Public declarations }
      end;

      TDias = (Domingo, Segunda, Terca, Quarta, Quinta, Sexta, Sabado);

      TDiasHelper = record helper for TDias
      function GetName: string;
      end;

      var
      Form1: TForm1;

      implementation

      {$R *.dfm}

      { TDiasHelper }

      function TDiasHelper.GetName: string;
      begin
      case Self of
      Domingo: Result := ‘Domingo’;
      Segunda: Result := ‘Segunda’;
      Terca : Result := ‘Terça’;
      Quarta : Result := ‘Quarta’;
      Quinta : Result := ‘Quinta’;
      Sexta : Result := ‘Sexta’;
      Sabado : Result := ‘Sábado’;
      end;
      end;

      end.

      Abraço.

  2. Tentando usar um class helper no FMX para um TEdit e não vai.
    Usava o mesmo esquema na VCL e funcionava normalmente. Bem estranho.

  3. Primeiramente gostaria de te dá os Parabéns!
    esses seus Classes Helper ficou top demais!

    Esses arquivos estão disponíveis em algum repositório ?
    poderia compartilhar a UNIT Helper.Integer ??

    1. Olá Danilo.

      Obrigado!

      Em breve, quando eu for escrever o artigo sobre a API REST usando Datasnap e padrões de projeto, esses arquivos estarão disponíveis nos fontes do projeto. Por enquanto, segue o fonte do class helper para Integer.

      Abraço.

      unit Helper.Integer;

      interface

      type
      TIntegerHelper = record helper for Integer
      procedure Add(const AValue: Integer);
      procedure Subtract(const AValue: Integer);
      function Equals(const AVal: Integer): Boolean;
      function Differs(const AVal: Integer): Boolean;
      function Pred: Integer;
      function Succ: Integer;
      function BiggerThan(const AInt: Integer): Boolean;
      function MinorThan(const AInt: Integer): Boolean;
      procedure Inc(const AStep: Integer = 1);
      procedure Dec(const AStep: Integer = 1);
      function ToString: string;
      function ToHex: string; overload;
      function ToHex(const ADigits: Integer): string; overload;
      end;

      implementation

      uses
      System.SysUtils;

      { TIntegerHelper }

      procedure TIntegerHelper.Add(const AValue: Integer);
      begin
      Self := Self + AValue;
      end;

      function TIntegerHelper.BiggerThan(const AInt: Integer): Boolean;
      begin
      Result := Self > AInt;
      end;

      procedure TIntegerHelper.Dec(const AStep: Integer);
      begin
      System.Dec(Self, AStep);
      end;

      function TIntegerHelper.Differs(const AVal: Integer): Boolean;
      begin
      Result := Self <> AVal;
      end;

      function TIntegerHelper.Equals(const AVal: Integer): Boolean;
      begin
      Result := Self = AVal;
      end;

      procedure TIntegerHelper.Inc(const AStep: Integer);
      begin
      System.Inc(Self, AStep);
      end;

      function TIntegerHelper.MinorThan(const AInt: Integer): Boolean;
      begin
      Result := Self < AInt; end; function TIntegerHelper.Pred: Integer; begin Result := System.Pred(Self); end; procedure TIntegerHelper.Subtract(const AValue: Integer); begin Self := Self - AValue; end; function TIntegerHelper.Succ: Integer; begin Result := System.Succ(Self); end; function TIntegerHelper.ToHex(const ADigits: Integer): string; begin Result := IntToHex(Self, ADigits); end; function TIntegerHelper.ToHex: string; begin Result := IntToHex(Self); end; function TIntegerHelper.ToString: string; begin Result := IntToStr(Self); end; end.

Deixe um comentário

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