Template Method: Refatorando com design patterns – Robson Castilho
Retornando aos design patterns, desta vez falarei sobre o padrão Template Method. No entanto, neste post, utilizarei uma abordagem diferente: ao invés de definir o padrão e dar um exemplo, vou mostrar como refatorar código com o auxílio do padrão, melhorando o design de uma aplicação já existente.
Imagine que na aplicação que esteja dando manutenção exista a classe abaixo, responsável por fazer o cálculo de uma prestação de um contrato:
public class Prestacao
public decimal Calcular(decimal valorTotal, int numeroDeParcelas)
var valorPrincipal = valorTotal / numeroDeParcelas;
var valorDaTaxaAdministrativa = valorPrincipal * 0.01m;
var valorDoSeguro = valorPrincipal * 0.015m;
return valorPrincipal + valorDaTaxaAdministrativa + valorDoSeguro;
A classe é utilizada com sucesso na aplicação até que, em um belo dia, é necessário calcular o valor da prestação de outra forma, para alguns contratos específicos. Então, alguém cria a classe abaixo:
public class PrestacaoContratoEspecial
var valorDaTaxaAdministrativa = (valorPrincipal * 0.01m) + 1.5m;
var valorDoSeguro = 0.015m / valorPrincipal + 1;
Pois bem. Percebam que ambas são bem parecidas, apresentando a mesma estrutura: 1) calcular o valor principal; 2) calcular a taxa administrativa; 3) calcular o seguro; 4) somar os 3 valores e retornar esta soma.
Imaginem agora que nos meses seguintes comecem a surgir novos contratos que exijam outros tipos de cálculos. O que faríamos? CTRL-C / CTRL-V e nova classe criada para o novo tipo de cálculo!
Assim, teríamos várias classes com comportamento semelhante esparramadas pela aplicação (sendo que as 2 exibidas acima ainda apresentam duplicação de código na primeira e na última linha).
PRIMEIRA REFATORAÇÃO
Esqueçam por um instante da simplicidade do exemplo acima e imaginem que cada passo do algoritmo (por ex: calcular o valor principal) fosse um pouco mais complexo. Poderíamos então refatorar o método Calcular, extraindo métodos para cada parte do algoritmo.
Vejamos como fica para a primeira classe "Prestacao":
private decimal _valorPrincipal;
private decimal _valorDaTaxaAdministrativa;
private decimal _valorDoSeguro;
CalcularValorPrincipal(valorTotal, numeroDeParcelas);
CalcularValorDaTaxaAdministrativa();
CalcularValorDoSeguro();
return CalcularValorTotal();
private void CalcularValorPrincipal(decimal valorTotal, int numeroDeParcelas)
_valorPrincipal = valorTotal / numeroDeParcelas;
private void CalcularValorDaTaxaAdministrativa()
_valorDaTaxaAdministrativa = _valorPrincipal * 0.01m;
private void CalcularValorDoSeguro()
_valorDoSeguro = _valorPrincipal * 0.015m;
private decimal CalcularValorTotal()
return _valorPrincipal + _valorDaTaxaAdministrativa + _valorDoSeguro;
Muito mais legível, não é? Lembrando que vocês veriam muito mais "vantagem" se cada passo fosse um pouco mais complexo.
Fazendo o mesmo para a classe PrestacaoContratoEspecial (exercício para vocês), teremos duas classes mais legíveis porém ainda com duplicação de código.
APLICANDO O PADRÃO
Com o padrão Template Method, podemos "refatorar para generalizar", ou seja, podemos mover comportamento comum para uma classe base para evitar código duplicado.
Além disso, deixamos o design mais extensível, criando tipos novos de prestação sempre que precisarmos (ao invés de termos de alterar algumas classes, enchendo-as de "if's").
Vejamos o que diz a intenção do padrão, segundo o GoF (Gang of Four – Design Patterns):
"Definir o esqueleto de um algoritmo em uma operação, postergando alguns passos para as subclasses. Template Method permite que subclasses redefinam certos passos de um algoritmo sem mudar a estrutura do mesmo."
Percebam novamente nos exemplos acima que temos uma sequencia de passos idêntica para o método Calcular de ambas as classes. Vamos então aplicar o padrão, criando uma classe abstrata para "abrigar" o método Calcular (que, de fato, é o nosso Template Method).
Chamaremos esta nova classe de "Prestacao" e a classe concreta "Prestacao" será renomeada para "PrestacaoContratoPadrao":
public abstract class Prestacao
protected decimal _valorPrincipal;
protected decimal _valorDaTaxaAdministrativa;
protected decimal _valorDoSeguro;
protected virtual void CalcularValorPrincipal(decimal valorTotal, int numeroDeParcelas)
protected abstract void CalcularValorDaTaxaAdministrativa();
protected abstract void CalcularValorDoSeguro();
protected virtual decimal CalcularValorTotal()
Vamos a algumas observações quanto à classe acima:
1) Uma vez que a intenção é variar os passos do algoritmo nas subclasses, não faz sentido sobrescrever o Template Method "Calcular". Portanto, nada de "virtual" nele.
2) Todos os passos do algoritmo (demais métodos) são protegidos, já que não faz sentido uma classe-cliente usar somente um "pedaço" do algoritmo. Somente o Template Method "Calcular" é público.
3) Como "CalcularValorPrincipal" e "CalcularValorTotal" eram idênticos nos 2 tipos de prestação, levamos a implementação deles para a classe-base. No entanto, deixei um gancho (virtual) para que possam ser sobrescritos.
4) Os métodos "CalcularValorDaTaxaAdministrativa" e "CalcularValorDoSeguro" são operações primitivas (abstract), ou seja, DEVEM ser implementados pelas subclasses.
Finalizamos, então, alterando as subclasses concretas "PrestacaoContratoPadrao" (antiga "Prestacao") e "PrestacaoContratoEspecial" para se adequar ao padrão:
public class PrestacaoContratoPadrao : Prestacao
protected override void CalcularValorDaTaxaAdministrativa()
protected override void CalcularValorDoSeguro()
public class PrestacaoContratoEspecial : Prestacao
_valorDaTaxaAdministrativa = (_valorPrincipal * 0.01m) + 1.5m;
_valorDoSeguro = 0.015m / _valorPrincipal + 1;
Notem agora que ambas as classes herdam de "Prestacao" e implementam somente os métodos que variam. Legal, não é?
Neste post, refatoramos duas classes semelhantes (poderiam ser diversas) utilizando o design pattern Template Method.
O bom conhecimento de orientação a objetos e design patterns nos auxiliam na melhoria do design do software, ajudando a eliminar bad smells como a duplicação de código e a deixar o código mais limpo e extensível.
Lembrando que não é preciso "forçar a barra" para usar um design pattern. Conheça bem a intenção do padrão e avalie se realmente haverá ganho naquele momento.
Obs.: O código-fonte está em: https://github.com/robsoncastilho/RefactoringTemplateMethod (Fiz o exemplo no Visual Studio 2012 RC com .Net 4.5, mas você pode criar o projeto no 2010 e usar as classes do exemplo).
design patterns, gof, orientacao a objetos, refactoring, template method
6 comentários em "Template Method: Refatorando com design patterns"
13/07/2012 às 07:42
Parabéns pelo artigo, você conseguiu explicar de forma bem clara e objetiva.
13/07/2012 às 19:56
Wallison Santos disse:
16/07/2012 às 12:35
Excelente artigo para fixar melhor a idéia teórica que eu tinha sobre esse pattern, mas nunca utilizei.
Obrigado, Wallison.
Agora é só praticar! (use com moderação)
30/05/2013 às 11:41
Com certeza entre os vários sites que pesquisei sobre padrões de projeto, o seu da de 10 a 0. Sempre muito bem explicado. Parabéns.