Template Method: Refatorando com design patterns

Olá, pessoal

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.

Vamos codar então!

O CENÁRIO

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
{
    public decimal Calcular(decimal valorTotal, int numeroDeParcelas)
    {
        var valorPrincipal = valorTotal / numeroDeParcelas;

        var valorDaTaxaAdministrativa = (valorPrincipal * 0.01m) + 1.5m;

        var valorDoSeguro = 0.015m / valorPrincipal + 1;

        return valorPrincipal + valorDaTaxaAdministrativa + valorDoSeguro;
    }
}

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”:

public class Prestacao
{
    private decimal _valorPrincipal;
    private decimal _valorDaTaxaAdministrativa;
    private decimal _valorDoSeguro;

    public decimal Calcular(decimal valorTotal, int numeroDeParcelas)
    {
        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;

    public decimal Calcular(decimal valorTotal, int numeroDeParcelas)
    {
        CalcularValorPrincipal(valorTotal, numeroDeParcelas);
        CalcularValorDaTaxaAdministrativa();
        CalcularValorDoSeguro();
        return CalcularValorTotal();
    }

    protected virtual void CalcularValorPrincipal(decimal valorTotal, int numeroDeParcelas)
    {
        _valorPrincipal = valorTotal / numeroDeParcelas;
    }

    protected abstract void CalcularValorDaTaxaAdministrativa();
    protected abstract void CalcularValorDoSeguro();

    protected virtual decimal CalcularValorTotal()
    {
       return _valorPrincipal + _valorDaTaxaAdministrativa + _valorDoSeguro;
    }
}

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()
    {
        _valorDaTaxaAdministrativa = _valorPrincipal * 0.01m;
    }

    protected override void CalcularValorDoSeguro()
    {
       _valorDoSeguro = _valorPrincipal * 0.015m;
    }
}

public class PrestacaoContratoEspecial : Prestacao
{
    protected override void CalcularValorDaTaxaAdministrativa()
    {
        _valorDaTaxaAdministrativa = (_valorPrincipal * 0.01m) + 1.5m;
    }

    protected override void CalcularValorDoSeguro()
    {
        _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 é?

CONCLUSÃ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).

Espero que tenham gostado. Comentem!

[]s

Anúncios

6 comentários em “Template Method: Refatorando com design patterns

  1. Excelente artigo para fixar melhor a idéia teórica que eu tinha sobre esse pattern, mas nunca utilizei.

  2. 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.

Participe! Vamos trocar uma ideia sobre desenvolvimento de software!

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s