Quantas vezes você gostaria de ter um novo método em uma classe padrão? Certamente não foram poucas e se já estiver a algum tempo trabalhando com Flutter/Dart. Helpers como também pode ser chamada em outras linguagens de programação, são implementações relativamente novas entre as linguagens e se propõe ir além da estrutura original desenhada pelo autor da classe.

Dart introduziu “extension” no ano passado (2019) e seu uso vem sendo ampliado pelos criadores de “package” como é o exemplo do pacote “Provider” que estendeu o BuildContext para incluír métodos read, watch, select como mecanismo para obter ou escutar alteração nos valores armazenados nas classes gerenciadas pelo “Provider”.

Anatomia de “extension” – a simplicidade

extension [novo nome] on [classe] {
}

Neste ponto os métodos que forem implementados na nova classe ira estender a referência a classe original, talvez fique mais claro com alguns exemplos.

extension StringExtension on String{ 
   int parseInt()=>int.parse(this);
}

A classe “StringExtension” agora estende a classe “String” para incluír o método “parseInt()” que converte uma string em int:

  var texto = '123';
  /// converte para int
  int  i =  texto.parseInt();
  print(i);

Por algum tempo fiquei pensando em uma solução para Class Helper ganhar poder de adicionar uma nova variável. Confesso que já estava desistindo, parecia não ser possível… mas – pensando bem….

Digamos que você tenha uma classe base que gostaria de fazer um Class Helper dela:

Exemplo de uma classe original:
[code]

TComponentX = class(TComponent)
public
published
end;

[/code]
(Code:A)

O desafio é como incluir uma nova variável a classe TComponentX utilizando de Class Helper…

Razões para usar um "Class Helper" e não uma herança: 
    se houver possibilidade de injetar a variável através de herança, 
    é provável que não irá precisar criar um "Class Helper" 
    - as razões para criar um "Class Helper" 
    é dispensar mudanças nos objetos em units já implementadas.

Agora, vamos usar o “Class Helper” para injetar uma nova variável à classe TComponentX:
[code]
TComponentHelper = Class Helper TComponent
public
Ligado:Boolean;
end;
[/code]
(Code:B)

Se estiver habituado ao uso de Class Helper, já deve ter notado que este exemplo (Code:B) não é compilado no Delphi – reclamando que um “Class Helper” não pode inserir uma nova variável. Não seja apressado, já lembrou que existe um “Class Var” e logo sugere escrever assim: ” class var Ligado:Boolean; “… Se este for o seu caso, não tenho boa notícia – você terá criado uma única referência para a variável, não podendo receber valores diferentes nas várias instâncias que a aplicação desejar manter carregada na memória – ou seja, criou uma simples variável GLOBAL que é compartilhada com todas as instâncias da classe;

No nosso caso, queremos que cada instância tenha o seu próprio valor, portanto não pode utilizar um “Class Var” como solução.

PROPOSTA:
[code]
unit Unit1;

interface

uses System.Classes, System.Generics.Collections;

type

TComponentHelper = Class Helper for TComponent
protected
function GetLigado: boolean;
procedure SetLigado(const AValue: boolean);
public
property Ligado: boolean Read GetLigado write SetLigado;
end;

implementation

var
LComponentLigado: TDictionary<TComponent, boolean>;

function TComponentHelper.GetLigado: boolean;
begin
result := true; // responde um padrão, para quando não existir;
if LComponentLigado.ContainsKey(self) then
result := LComponentLigado.Items[self]; // pega o valor da lista
end;

procedure TComponentHelper.SetLigado(const AValue: boolean);
begin
LComponentLigado.AddOrSetValue(self, AValue); // inclui na lista
end;

initialization

LComponentLigado := TDictionary<TComponent, boolean>.Create;

finalization

LComponentLigado.free;

end.
[/code]

A implementação permite adicionar novas variáveis ao objeto do “Class Helper” sem interferir no funcionamento da classe padrão.

Por algum tempo não dei muita atenção para a RTTI. Tudo era muito trabalhoso. Quando cheguei na família XE notei que as coisa tinham mudado bastante, então passei a fazer uso de umas coisas aqui.. outras ali… quando nem tinha me dado conta as coisas estavam ficando sérias.

RTTI é uma ferramenta poderosa, mas dá trabalho. Gostaria de simplificar um pouco as coisa para poder usar com mais frequência e com mais segurança.

Depois de várias tentativas concluí que o caminha mais rápido seria usar Class Helper para entregar ao TObject suporte mais facilitado para as chamadas RTTI.

 

[code lang=”pascal”]
TObjectHelper = class helper for TObject
….
// RTTI
property Properties[AName: string]: TValue read GetProperties
write SetProperties;
property Fields[AName: string]: TValue read GetFields write SetFields;
property Methods[AName: String]: TRttiMethod read GetMethods;
function HasAttribute(aMethod: TRttiMethod;
attribClass: TCustomAttributeClass): Boolean;
function InvokeAttribute(attribClass: TCustomAttributeClass;
params: array of TValue): Boolean;
function InvokeMethod(AName: string; params: array of TValue): Boolean;

end;

[/code]

Ver classe completa: RTTI Class Helper
* alguns métodos foram alterados para resolver conflitos.
 

Exemplo:

 

[code]
{$R *.dfm}
uses System.Classes.helper, System.TypInfo;

procedure TForm3.Button1Click(Sender: TObject);
begin
Button1.GetPropertiesList( ListBox1.Items ); // pega uma lista de properiedades do Button1
edit2.Text := Button1.Properties[‘Caption’].AsString; // pega o valor da propriedade caption
end;

procedure TForm3.Button2Click(Sender: TObject);
begin
button1.Properties[ ‘Caption’ ] := edit2.Text; // altera a proprieda do Caption
end;

procedure TForm3.Button3Click(Sender: TObject);
begin
button1.GetFieldsList( ListBox2.Items, [mvPrivate,mvPublic] );
end;

[/code]

Ver Exemplos

 

.