sexta-feira, 17 de outubro de 2008

Eliminado duplicação de código com delegação

É fácil eliminar a duplicação de código quando o bloco código repetido é contínuo, ou seja, não há nenhum trecho dentro do bloco que varia. Neste caso, um simples Extract Method resolveria o problema. Mas o que fazer se os trechos são muito semelhantes mas tem uma(s) linha(s) que são diferentes, como no caso abaixo?


function TMinhaClasse.GetSumVr_Contabil: Double;
var
CloneItems: TClientDataSet;
begin
Result:= 0;
CloneItems := TClientDataSet.Create(nil);
try
CloneItems.CloneCursor(MeuDataSet, True);
while not CloneItems.eof do
begin
Result := Result + CloneItems.FieldByName('vr_contabil').AsFloat;

CloneItems.Next;
end;
finally
CloneItems.Free;
end;
end;

function TMinhaClasse.GetSumVr_Desconto: Double;
var
CloneItems: TClientDataSet;
begin
Result:= 0;
CloneItems := TClientDataSet.Create(nil);
try
CloneItems.CloneCursor(MeuDataSet, True);
while not CloneItems.eof do
begin
Result := Result + CloneItems.FieldByName('vr_desconto').AsFloat;

CloneItems.Next;
end;
finally
CloneItems.Free;
end;
end;


Bem, este caso é muito simples. Portanto, a resposta parece óbvia: cria-se um novo método para o qual passamos como parâmetro o nome do campo a ser sumarizado. É verdade. Mas se o processamento envolvesse mais do que uma simples linha de código, a resposta poderia ser bem diferente. Como proceder? Usar delegação (em delphi, ponteiro para uma função).

Declaramos o tipo de função que esperamos receber como parâmetro na função que fará o processamento:


type
TCalcFunc = function (const Value: Double; DataSet: TDataSet): Double of object;


Criamos um novo método com toda a lógica do processamento. E este método receberá como parâmetro um ponteiro para a função que irá fazer o processamento. Isto eliminará a duplicação de código, reduzindo a possibilidade de erros advindos de alterações na lógica (Não é raro esquecermos de alterar o código copiado/colado):


function TMinhaClasse.GetSum(CalcFunc: TCalcFunc): Double;
var
CloneItems: TClientDataSet;
begin
Result:= 0;
CloneItems := TClientDataSet.Create(nil);
try
CloneItems.CloneCursor(MeuDataSet, True);
CloneItems.First;
while not CloneItems.Eof do
begin
Result := CalcFunc(Result, CloneItems);

CloneItems.Next;
end;
finally
CloneItems.Free;
end;
end;


Agora modificamos as funcões antigas para adaptá-las à nova metodologia:


function TMinhaClasse.GetSumVr_Contabil: Double;
begin
Result := GetSum(CalcVrContabil);
end;

function TMinhaClasse.GetSumVr_Desconto: Double;
begin
Result := GetSum(CalcVrDesconto);
end;

function TMinhaClasse.CalcVrContabil(const Value: Double; DataSet: TDataSet): Double;
begin
Result := Value + DataSet.FieldByName('vr_contabil').AsFloat;
end;

function TMinhaClasse.CalcVrDesconto(const Value: Double; DataSet: TDataSet): Double;
begin
Result := Value + DataSet.FieldByName('vr_desconto').AsFloat;
end;


Agora ficou fácil adicionar novas funções aproveitando a mesma lógica: GetSumVr_Frete(), GetSumVr_ICMS(), etc.

Outra maneira de fazer a mesma coisa seria usando design patterns. Mas, às vezes a coisa é tão simples que aplicar um ou vários design patterns seria como usar um martelo para matar uma formiguinha, levando a um excesso de sofisticação na engenharia.

Aguardo os seus comentários e críticas. Ok?

Um comentário:

OgreT disse...

Vo tentar fazer esse exemplo ai na prática; Ei maxu...num daria pra tu botar as "strings" de uma cor diferente nao?! Ficaria melhor de ler o exemplo...

 

web analytics software