Class/Record Helpers. Ato Final.

E ae brutaiada?

Vou começar essa terceira, e tomara que última, parte falando duas coisas que eu não mencionei nos posts anteriores. Com class\record helpers é possível alterar os valores ou propriedades do objeto. Até agora todos os exemplos que eu dei simplesmente retornavam valores. Outra coisa é que não é possível ter dois helpers ativos para a mesma classe, logo vocês vão entender a importância dessas duas informações.

Fazendo um revival rápido nos posts anteriores da série, vimos como criar um helper simples e aprendemos algumas funcionalidades do SysUtils.TStringHelper. A maioria dos outros Helpers são de tipos numéricos, nada de tão excepcional quanto o StringHelper. O único detalhe é que todos eles têm as constantes MinValue e MaxValue que indicam, respectivamente, os valores mínimos e máximos aceitos por esses tipos. De toda forma, vale a pena você dar uma olhada neles.

Agora um helper que é completamente inútil é o TBooleanHelper: converter pra inteiro, conversão para string, tamanho… nada que seja útil. Então, se não serve, vamos fazer o nosso. Não gosto muito dessa viadagem de análise (quem faz análise é psicólogo), mas vamos pensar um pouco antes de meter a mão na massa: “O que pode vir a ser útil em um helper para Boolean?” A primeira coisa que me vem à mente é uma conversão para String útil: Sim/Não, S/N. Vamos criar então duas functions com o mesmo nome (overload). Uma sem parâmetros que vai retornar sempre sim ou não, que para mim é o mais utilizado sempre  e outra com dois parâmetros strings, um para o sim e outro para o não, caso desejarmos retornar algo diferente de sim e não.

type 
TBooleanHelper = record helper for Boolean
Public
  function ToString: String; overload;
  function ToString(Sim,Nao: String):String; overload;
end;

Agora sua mente fervilha de dúvidas: “Por quê nos outros você sempre escreveu class helper for, e nesse foi record helper for?” Simples, meu pequeno padawan, o compilador, não sei por qual motivo, trata tipos nativos como registros (records) e não classes… então você tem que declarar um helper de registro e não de classe para os tipos nativos (e para registros também). E o que é overload? Bem, isso não faz parte da nossa aula mas vou explicar por cima. Em uma classe quando você tem duas funções ou procedures com o mesmo nome e parâmetros de entrada diferentes, para o Delphi dar conta dos dois você precisa escrever overload na frente dos dois. Nós chamamos isso de polimorfismo e é um conceito de OO. Para maiores detalhes o Patrão está com um ótimo curso de OO no blog.  Acesse AQUI.

Mas cadê a implementação??? Calma jovem precoce… no momento estamos apenas planejando o nosso helper. No final, quando soubermos tudo o que fazer, vamos meter a mão na massa. Bem, até agora nosso helper só tem uma utilidade. O que mais poderíamos fazer pra ele ficar supimpa? Não sei vocês, mas eu simplesmente odeio usar not em if, tem que colocar antes, abrir parênteses na maioria das vezes, não fica natural, nem legal. Então eu vou adicionar no nosso helper uma função de negação. Ai vamos poder fazer algo como:

If (rgTipoDocumento.ItemIndex = 0).not then

E já que nós estamos no embalo, porque não tentar com o And ou o Or? Pode ficar legal, não?

If (rgDocumento.ItemIndex = 0).and(cbxNegativo. Checked) then
If (rgDocumento.ItemIndex = 0).or(rgDocumento.ItemIndex = 1) then

Só que nós temos um problema: not, and e or são palavras reservadas, e normalmente não conseguiríamos declarar as nosass funções com esse nomes. Mas o bruto tem um truque na mão: &. Isso mesmo incrédulo leitor. Se declaramos as funções com & antes do nome, podemos usar palavras reservadas como nomes de nossos metodos:

type 
TBooleanHelper = record helper for Boolean
Public
function ToString: String; overload;
function ToString(Sim,Nao: String):String; overload;
function &Not: Boolean;
function &And(Arg: Boolean): Boolean;
function &Or(Arg: Boolean): Boolean;
end;

Bem, parece que acabamos né? Bom, pensando bem, não seria legal se pudéssemos alterar o valor da nossa variável, negando-a com ajuda do nosso Helper? Ao invés de fazer: variavel := not variavel, usar variavel.not? Só que agora teremos que inventar um nome, pois já utilizamos not na funcionalidade anterior. Para resolver, vou chamar a nossa procedure de aNot (a de altera). Não vou fazer para o And e para o Or que eu acho meio sem sentido, mas se você acha que será útil e quiser fazer, boa sorte!

type 
TBooleanHelper = record helper for Boolean
Public
  function ToString: String; overload;
  function ToString(Sim,Nao: String):String; overload;
  function &Not: Boolean;
  function &And(Arg: Boolean): Boolean;
  function &Or(Arg: Boolean): Boolean;
  procedure aNot;
end;

Nesse ponto você deve estar se perguntando: “procedure Bruto? A gente só usou função até agora!” É, isso mesmo, procedure meu caro amigo. Nós vamos alterar o valor da variável e não vamos retornar nada… então a melhor escolha é a procedure. Bem, agora para deleite do apressadinho de lá de cima, eis a implementação do nosso helper:

interface

type
TBooleanHelper = record helper for Boolean
Public
  function ToString: String; overload;
  function ToString(Sim,Nao: String):String; overload;
  function &Not: Boolean;
  function &And(Arg: Boolean): Boolean;
  function &Or(Arg: Boolean): Boolean;
  procedure aNot;
end;

implementation

{ TBooleanHelper }

procedure TBooleanHelper.aNot;
begin
  Self := Self.⫬
end;

function TBooleanHelper.&And(Arg: Boolean): Boolean;
begin
  Result := Self and Arg;
end;

function TBooleanHelper.&Not: Boolean;
begin
  Result := not Self;
end;

function TBooleanHelper.&Or(Arg: Boolean): Boolean;
begin
  Result := Self or Arg;
end;

function TBooleanHelper.ToString: String;
begin
  Result := Self.ToString('Sim','Não');
end;

function TBooleanHelper.ToString(Sim, Nao: String): String;
begin
  if Self then
    Result := Sim
  else
    Result := Nao
end;

Acho que não devem surgir muitas dúvidas na implementação é um código fácil, mas vou reforçar alguns pontos que podem ter confundido vocês. Na implementação do aNot, eu já me aproveitei da função &Not do nosso próprio helper. Na função toString, usei uma pequena mágica que o overload nos permite fazer: Chamei a função com parâmetros, a partir da função sem parâmetros. Ahhhh, mais um detalhe antes de acabarmos! Como eu disse lá em cima, a partir do momento que você dar um uses nessa unit, o helper padrão ficará indisponível, (na verdade, o Delphi considera como ativo o helper que estiver declarado por último, então se a sua unit vier antes da SysUtils, valerá o helper padrão).

Então é isso… acaba por aqui as nossas aulas de class\rercord helpers. Mas não se preocupem, o Bruto não vai deixar vocês órfãos… já já eu to de volta com outras coisas legais dessa nossa maravilhosa ferramenta.