A primeira fase do refactoring é dividir o método e mover as partes para classes que se adaptam melhor. O objetivo principal é facilitar a criação do método RealtorioHTML com a menor duplicação de código possível.
O primeiro passo é encontrar um amontoado lógico de código e utilizar a Extração de Método. Uma parte de código óbvia são as sequências de if-else. Essa para ser uma boa parte de código para ser isolada e ter seu próprio método.
Quando extraímos um método, assim como em qualquer outro refactoring, precisamos saber o que pode dar errado. Se fizermos uma má extração, poderemos inserir um bug ao programa. Então, antes de fazer o refactoring, precisamos pensar em como fazer isso com segurança. Para isso precisamos seguir os itens de segurança.
Primeiro precisamos localizar e identificar no fragmento de código todas as variáveis locais e parâmetros. No trecho de código que vamos extrair identificamos as seguintes variáveis: Locacao e TotalLocacao. A variável Locacao não é modificada pelo código, mas Contador e TotalLocacao são modificadas. Para qualquer variável que não é modificada pelo código eu posso defini-la no novo método como um parâmetro. Variáveis que sofrem modificação precisam de um cuidado maior. Se houver somente uma variável que sofre modificação, podemos retorna-la como resultado do método. A variável Contador, apesar de sofre modificação durante o código, ela não faz parte da regra de negócio, ela é apenas um artifício para navegar entre os itens do objeto Locacao, portanto não há necessidade de retorna-la fora do método. Sendo assim, posso definir a variável TotalLocacao como retorno do método.
A seguir mostramos o código antes e depois do refactoring. Destacaremos em negrito as modificações.
Antes
|
Depois
|
function TCliente.Relatorio: string; var Total, TotalLocacao: Double; PontosCliente: Integer; Resultado: String; Contador: Integer; Locacao: TLocacao; begin Total := 0; PontosCliente := 0; Resultado := 'Registro de locação de ' + Nome + #13; // Determina total de cada locação for Contador := 0 to Locacoes.Count - 1 do begin Locacao := Locacoes.Items[Contador]; TotalLocacao := 0; if Locacao.Filme.CodigoPreco = TFilme.Regular then begin TotalLocacao := TotalLocacao + 2; if (Locacao.DiasAluguel > 2) then TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 2) * 1.5); end else if Locacao.Filme.CodigoPreco = TFilme.Lancamento then begin TotalLocacao := TotalLocacao + (Locacao.DiasAluguel * 3); end else if Locacao.Filme.CodigoPreco = TFilme.Infantil then begin TotalLocacao := TotalLocacao + 1.5; if (Locacao.DiasAluguel > 3) then TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 3) * 1.5); end; // Adiciona pontos de clientes PontosCliente := PontosCliente + 1; // Gera bonus porlocação de lançamento por dois dias if (Locacao.Filme.CodigoPreco = TFilme.Lancamento) and (Locacao.DiasAluguel > 1) then PontosCliente := PontosCliente + 1; //Mostra dados dessa locação Resultado := Resultado + '|' + Locacao.Filme.Titulo + '|' + FloatToStr(TotalLocacao) + #13; Total := Total + TotalLocacao; end; //Adiciona Rodapé Resultado := Resultado + 'O total é: ' + FloatToStr(Total) + #13; Resultado := Resultado + 'Você ganhou: ' + FloatToStr(PontosCliente) + ' pontos'; Result := Resultado; end; |
function TCliente.Relatorio: string; var Total, TotalLocacao: Double; PontosCliente: Integer; Resultado: String; Contador: Integer; Locacao: TLocacao; begin Total := 0; TotalLocacao := 0; PontosCliente := 0; Resultado := 'Registro de locação de ' + Nome + #13; // Determina total de cada locação for Contador := 0 to Locacoes.Count - 1 do begin Locacao := Locacoes.Items[Contador]; TotalLocacao := TotalLocacaoItem(Locacao); // Adiciona pontos de clientes PontosCliente := PontosCliente + 1; // Gera bonus porlocação de lançamento por dois dias if (Locacao.Filme.CodigoPreco = TFilme.Lancamento) and (Locacao.DiasAluguel > 1) then PontosCliente := PontosCliente + 1; //Mostra dados dessa locação Resultado := Resultado + '|' + Locacao.Filme.Titulo + '|' + FloatToStr(TotalLocacao) + #13; Total := Total + TotalLocacao; end; //Adiciona Rodapé Resultado := Resultado + 'O total é: ' + FloatToStr(Total) + #13; Resultado := Resultado + 'Você ganhou: ' + FloatToStr(PontosCliente) + ' pontos'; Result := Resultado; end; function TCliente.TotalLocacaoItem(Locacao: TLocacao): Double; var TotalLocacao: Double; begin TotalLocacao := 0; if Locacao.Filme.CodigoPreco = TFilme.Regular then begin TotalLocacao := TotalLocacao + 2; if (Locacao.DiasAluguel > 2) then TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 2) * 1.5); end else if Locacao.Filme.CodigoPreco = TFilme.Lancamento then begin TotalLocacao := TotalLocacao + (Locacao.DiasAluguel * 3); end else if Locacao.Filme.CodigoPreco = TFilme.Infantil then begin TotalLocacao := TotalLocacao + 1.5; if (Locacao.DiasAluguel > 3) then TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 3) * 1.5); end; Result := TotalLocacao; end; |
Quando fazemos uma mudança desse nível, temos que compilar e executar o teste. Após executar, todos os teste rodaram sem erros.
Digamos que na criação do método TotalLocacaoItem, tivéssemos colocar o retorno do tipo Integer ao invés de Double. Nesse caso, seria fácil pegar o problema no momento da compilação que estaria acusando erro de tipo de retorno. Esse é a essência do processo de refactoring pois o seria fácil e rápido encontrar o motivo do problema.
Então fica a dica: Refatore o programa em pequenos passos. Se você cometer um erro, será fácil encontrar o bug.
Na ferramenta Delphi XE3 temos o menu Refactor. (Não sei a partir de qual versão do Delphi já possui esse recurso). Se utilizarmos o item Extract Method (Shift+Ctrl+M), ele fará esse trabalho de extrair o método, gerando um novo método com o código selecionado. Conforme as imagens abaixo:
Gerando o código abaixo:
function TCliente.Relatorio: string;
var
Total, TotalLocacao: Double;
PontosCliente: Integer;
Resultado: String;
Contador: Integer;
Locacao: TLocacao;
begin
Total := 0;
PontosCliente := 0;
Resultado := 'Registro de locação de ' + Nome + #13;
// Determina total de cada locação
for Contador := 0 to Locacoes.Count - 1 do
begin
Locacao := Locacoes.Items[Contador];
TotalLocacaoItem(TotalLocacao, Locacao);
// Adiciona pontos de clientes
PontosCliente := PontosCliente + 1;
// Gera bonus porlocação de lançamento por dois dias
if (Locacao.Filme.CodigoPreco = TFilme.Lancamento) and (Locacao.DiasAluguel > 1) then
PontosCliente := PontosCliente + 1;
//Mostra dados dessa locação
Resultado := Resultado + '|' + Locacao.Filme.Titulo + '|' +
FloatToStr(TotalLocacao) + #13;
Total := Total + TotalLocacao;
end;
//Adiciona Rodapé
Resultado := Resultado + 'O total é: ' + FloatToStr(Total) + #13;
Resultado := Resultado + 'Você ganhou: ' + FloatToStr(PontosCliente) + ' pontos';
Result := Resultado;
end;
procedure TCliente.TotalLocacaoItem(var TotalLocacao: Double; Locacao: TLocacao);
begin
TotalLocacao := 0;
if Locacao.Filme.CodigoPreco = TFilme.Regular then
begin
TotalLocacao := TotalLocacao + 2;
if (Locacao.DiasAluguel > 2) then
TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 2) * 1.5);
end
else if Locacao.Filme.CodigoPreco = TFilme.Lancamento then
begin
TotalLocacao := TotalLocacao + (Locacao.DiasAluguel * 3);
end
else if Locacao.Filme.CodigoPreco = TFilme.Infantil then
begin
TotalLocacao := TotalLocacao + 1.5;
if (Locacao.DiasAluguel > 3) then
TotalLocacao := TotalLocacao + ((Locacao.DiasAluguel - 3) * 1.5);
end;
end;
E os testes continuam rodando sem erros:
Continua no próximo post...
Nenhum comentário:
Postar um comentário