Olá
Hoje irei falar de um recurso muito interessante, porém acredito ainda ser pouco utilizado pelos desenvolvedores Delphi. Estou falando do Anonymous Method (Métodos Anônimos).
Este recurso é assim definido no site da Embarcadero:
Como o nome sugere, um método anônimo é um procedimento ou função que não tem um nome associado a ele. Um método anônimo trata de um bloco de código como uma entidade que pode ser atribuído a uma variável ou usado como um parâmetro para um método. Além disso, um método anônimo pode se referir a variáveis e ligar os valores às variáveis no contexto em que o método é definido. Métodos anônimos pode ser definido e utilizado com a sintaxe simples.
O type do método anônimo é declarado como uma referência a um método:
type
TFuncOfInt = reference to function(x: Integer): Integer;Esta declaração indica que o método anônimo:
– é uma função
– recebe um parâmetro inteiro
– retorna um valor inteiro.
Com base nesta definição (e você que é leitor assíduo deste blog já sabe :)), iremos colocar isso em prática utilizando um exemplo simples (que não deve ser encarado como um caso real mas sim para efeitos didáticos). Neste exemplo, utilizando ainda o Delphi XE2, tentarei simular o que acontece geralmente em nossos sistemas quando não nos preocupamos com a repetição de código. Vamos lá!
Nosso exemplo irá utilizar um Form, quatro botões (para as operações soma, média, maior e menor) e um memo (que mostrará os passos executados), como pode ser visto na imagem abaixo:
Iremos utilizar um objeto chamado Lista (TStringList):
[sourcecode language=”delphi”]
private
{ Private declarations }
Lista: TStringList;
[/sourcecode]
No Oncreate do nosso form, criamos o objeto e adicionamos algumas strings:
[sourcecode language=”delphi”]
procedure TForm1.FormCreate(Sender: TObject);
begin
Lista := TStringList.Create;
Lista.Add(‘1’);
Lista.Add(‘5’);
Lista.Add(‘3’);
Lista.Add(‘9’);
Lista.Add(‘7′);
Lista.Add(’16’);
Lista.Add(’10’);
end;
[/sourcecode]
No OnDestroy, destruimos a lista:
[sourcecode language=”delphi”]
procedure TForm1.FormDestroy(Sender: TObject);
begin
Lista.Free;
end;
[/sourcecode]
No Onclick do primeiro botão, faremos:
[sourcecode language=”delphi”]
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
soma: integer;
begin
soma := 0;
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to Lista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
soma := soma + StrToInt(Lista.Strings[i]);
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
Memo1.Lines.Add(‘Soma: ‘+inttostr(soma));
end;
[/sourcecode]
No código acima, iremos percorrer a lista de strings, pegar a string, converter para inteiro e o resultado será somado à variável soma (não diga?!). Por fim, o memo será atualizado com o resultado da operação.
Executando o aplicativo e clicando no primeiro botão (soma), obteremos o seguinte:
Vamos agora fazer a codificação dos demais botões:
[sourcecode language=”delphi”]
//Média da lista
procedure TForm1.Button2Click(Sender: TObject);
var
i: integer;
soma: integer;
media: Real;
begin
soma := 0;
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to Lista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
soma := soma + StrToInt(Lista.Strings[i]);
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
media := soma / Lista.Count;
Memo1.Lines.Add(‘Média: ‘+formatfloat(‘,0.000’, media));
end;
//O maior número da lista
procedure TForm1.Button3Click(Sender: TObject);
var
i: integer;
maior: integer;
begin
maior := 0;
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to Lista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
if maior<StrToInt(Lista.Strings[i]) then
maior := StrToInt(Lista.Strings[i]);
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
Memo1.Lines.Add(‘Maior: ‘+inttostr(maior));
end;
//O menor número da Lista
procedure TForm1.Button4Click(Sender: TObject);
var
i: integer;
menor: integer;
begin
menor := StrToInt(Lista.Strings[0]);
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to Lista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
if menor>StrToInt(Lista.Strings[i]) then
menor := StrToInt(Lista.Strings[i]);
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
Memo1.Lines.Add(‘Menor: ‘+inttostr(menor));
end;
[/sourcecode]
É fácil perceber a repetição exagerada de código, principalmente no loop. Como trata-se de um único form, rapidamente detectamos o problema. Mas quem já programa, desenvolvendo grandes sistemas comerciais por exemplo, sabe que isso é mais comum do que se imagina. Volta e meia estamos lá, gerando mais um loop em nossa lista, mais uma repetição em nosso código. Pode ser numa lista de strings, num dataset, num array, etc.
Existem alguns meios de se evitar este problema. E um deles, certamente, é por meio da utilização de Anonymous Methods.
O primeiro passo na utilização dos métodos anônimos é declarar na seção type o nome do nosso método (aqui, irei utilizar um outro form, para manter a versão sem o recurso em questão, mas vocês poderão utilizar o mesmo form):
[sourcecode language=”delphi”]
type
TAnonymLista = reference to procedure(a: Integer);
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
…
[/sourcecode]
Note que o nosso método terá um parâmetro integer, que será o índice atual da lista de strings. Agora, iremos criar uma procedure que irá ter como um dos parâmetros, o nosso método anônimo:
[sourcecode language=”delphi”]
procedure PercorreLista(ALista: TStringList; AnonymLista: TAnonymLista);
var
i: integer;
begin
with Form2 do
begin
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to ALista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
AnonymLista(i); //método anônimo!!!
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
end;
end;
[/sourcecode]
Em cada loop, o AnonymLista é executado, passando o índice atual da lista que estamos percorrendo. Falta ainda alterar o código dos botões. Vamos começar com o primeiro botão:
[sourcecode language=”delphi”]
procedure TForm2.Button1Click(Sender: TObject);
var
soma: integer;
begin
soma := 0;
PercorreLista(Lista, procedure (a:Integer)
begin
soma := soma + strtoint(lista.strings[a]);
end);
Memo1.Lines.Add(‘Soma: ‘+inttostr(soma));
end;
[/sourcecode]
Em vez de chamar o loop (for), chamamos a procedure PercorreLista, passando a lista e, finalmente, passando um método (veja que não tem nome) com as instruções do que deve ser feito.
Código dos demais botões:
[sourcecode language=”delphi”]
procedure TForm2.Button2Click(Sender: TObject);
var
soma: integer;
media: Real;
begin
soma := 0;
PercorreLista(Lista, procedure(a:Integer)
begin
soma := soma + StrToInt(Lista.Strings[a]);
end);
media := soma / Lista.Count;
Memo1.Lines.Add(‘Média: ‘+formatfloat(‘,0.000’, media));
end;
procedure TForm2.Button3Click(Sender: TObject);
var
maior: integer;
begin
maior := 0;
PercorreLista(lista, procedure(a:Integer)
begin
if maior<StrToInt(Lista.Strings[a]) then
maior := StrToInt(Lista.Strings[a]);
end);
Memo1.Lines.Add(‘Maior: ‘+inttostr(maior));
end;
procedure TForm2.Button4Click(Sender: TObject);
var
menor: integer;
begin
menor := StrToInt(Lista.Strings[0]);
PercorreLista(Lista, procedure(a:Integer)
begin
if menor>StrToInt(Lista.Strings[a]) then
menor := StrToInt(Lista.Strings[a]);
end);
Memo1.Lines.Add(‘Menor: ‘+inttostr(menor));
end;
[/sourcecode]
Como pode ser visto, eu padronizei a utilização da minha lista. O que se repetia, mandei para procedure PercorreLista.
Segue código completo do exemplo:
[sourcecode language=”delphi”]
type
TAnonymLista = reference to procedure(a: Integer);
TForm2 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
Button4: TButton;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
Lista: TStringList;
public
{ Public declarations }
end;
procedure PercorreLista(ALista: TStringList; AnonymLista: TAnonymLista);
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure PercorreLista(ALista: TStringList; AnonymLista: TAnonymLista);
var
i: integer;
begin
with Form2 do
begin
memo1.lines.Clear;
Memo1.Lines.Add(‘Iniciando processo…’);
for i := 0 to ALista.Count – 1 do
begin
Memo1.Lines.Add(‘Item ‘ + inttostr(i) + ‘: ‘ + lista.Strings[i]);
AnonymLista(i);
end;
Memo1.Lines.Add(‘Processo finalizado’);
Memo1.Lines.Add(‘Linhas processadas: ‘+inttostr(lista.Count));
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
soma: integer;
begin
soma := 0;
PercorreLista(Lista, procedure(a:Integer)
begin
soma := soma + strtoint(lista.strings[a]);
end);
Memo1.Lines.Add(‘Soma: ‘+inttostr(soma));
end;
procedure TForm2.Button2Click(Sender: TObject);
var
soma: integer;
media: Real;
begin
soma := 0;
PercorreLista(Lista, procedure(a:Integer)
begin
soma := soma + StrToInt(Lista.Strings[a]);
end);
media := soma / Lista.Count;
Memo1.Lines.Add(‘Média: ‘+formatfloat(‘,0.000’, media));
end;
procedure TForm2.Button3Click(Sender: TObject);
var
maior: integer;
begin
maior := 0;
PercorreLista(lista, procedure(a:Integer)
begin
if maior<StrToInt(Lista.Strings[a]) then
maior := StrToInt(Lista.Strings[a]);
end);
Memo1.Lines.Add(‘Maior: ‘+inttostr(maior));
end;
procedure TForm2.Button4Click(Sender: TObject);
var
menor: integer;
begin
menor := StrToInt(Lista.Strings[0]);
PercorreLista(Lista, procedure(a:Integer)
begin
if menor>StrToInt(Lista.Strings[a]) then
menor := StrToInt(Lista.Strings[a]);
end);
Memo1.Lines.Add(‘Menor: ‘+inttostr(menor));
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
Lista := TStringList.Create;
Lista.Add(‘1’);
Lista.Add(‘5’);
Lista.Add(‘3’);
Lista.Add(‘9’);
Lista.Add(‘7′);
Lista.Add(’16’);
Lista.Add(’10’);
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
Lista.Free;
end;
[/sourcecode]
Agora eu pergunto:
– Quantas vezes você programou o mesmo loop (com alguma alteração em seu interior) em partes distintas da sua aplicação?
– Quantas vezes você percorreu um dataset, seja para imprimir determinado registro seja para pegar o total de um campo?
– Já pensou em otimizar estes processos?
O Anonymous Methods poderia ser uma opção a ser analisada. Não estou dizendo que é a única forma, claro existem outras, mas é um recurso interessante.
Espero que tenham gostado. Se sim, clique em curtir na caixa do Facebook na lateral.
Abraços.
Luiz, sou programador há vários anos, já programei em Clipper, Cobol e outros, hoje programo em Delphi e quero deixar aqui meus parabéns a você pelo excelente blog, tenho me atualizado com relação as novas tecnologias incorporadas ao Delphi e este é um dos sites que muito tem me ajudado. Parabéns !
Obrigado Claudius.
Fico feliz que o conteúdo deste blog tenha lhe ajudado.
Ultimamente estou um pouco afastado do blog, muito trabalho pendente, mas sempre que posso, coloco algo para os meus leitores.
Sugiro que leia a série ORM Básico. Tem muita coisa interessante.
Abraços.
SHOW DE BOLA
Parabéns pela abordagem. Ajudou muito, mesmo tantos anos depois de vc publicar. Abraço!
Lá se vão 7 anos! Passa rápido. 🙂