Voltando no assunto do princípio da responsabilidade única, eu já havia dito para dar aos seus métodos uma única razão para mudar. Há um motivo a mais: acoplamento temporal.

Isso acontece quando você tem uma função que faz mais de uma coisa, ou seja, tem mais de um motivo para mudar.

Por exemplo, você tem uma classe produto, que tem uma propriedade "Preco". Alguém chama o método "EntrarEmPromocao". Esse método calcula o percentual de desconto com base em algum cálculo complexo, e aplica o desconto na propriedade "Preco".

Pronto, você tornou sua classe mais difícil de testar. Como eu faço para testar somente o cálculo de desconto? Preciso configurar o preço antes? Ok, separo o cálculo:

void CriarDesconto()
{
    //algum calculo, que no final seta:
    this.Preco -= algumValor;
}
public void EntrarEmPromocao()
{
    CriarDesconto();
    //faz outras coisas relacionadas à promoção
}

Agora está separado. Mas o método CriarDesconto ainda faz muita coisa. Ok, então você separa mais. Cria um método "CriarDesconto", e outro "AplicarDesconto", assim:

private decimal _desconto;
void CriarDesconto()
{
    //algum calculo, que no final seta:
    _desconto = algumValor;
}
void AplicarDesconto()
{
    this.Preco -= _desconto;
}

Depois disso é só chamar no método EntrarEmPromocao:

public void EntrarEmPromocao()
{
    CriarDesconto();
    AplicarDesconto();
}

Ainda assim: como eu testo isso? Tenho que checar a variável privada _desconto? Complicado… Além disso, em que ordem as funções devem ser chamadas? Primeiro eu aplico, ou primeiro eu crio? Até faz algum sentido, mas comecem a imaginar isso em alguns domínios mais complexos…

O jeito é criar um acoplamento temporal, as classes são obrigadas a trabalhar juntas durante um tempo para obter o resultado desejado:

decimal ObterDesconto()
{
    //algum calculo, que no final retorna:
    return algumValor;
}
void AplicarDesconto(decimal desconto)
{
    this.Preco -= desconto;
}

Pronto, não tem como chamar AplicarDesconto sem saber o valor do desconto. Tudo resolvido. A chamada fica clara, assim:

public void EntrarEmPromocao()
{
    var desconto = ObterDesconto();
    AplicarDesconto(desconto);
}

Com isso, a função ObterDesconto é plenamente testável, e não tem mais efeitos colaterais.

Efeitos colaterais são coisas que acontecem quando você não esperava. Você chamava CriarDesconto e ela setava o valor do preço. Isso é um efeito colateral. Agora há responsabilidades claras. Um método obtem o desconto, o outro aplica. Posso testar separadamente.


Postado na(s) categoria(s) Arquitetura , Dicas pelo giovanni bassi em 23 de fevereiro de 2009 às 11:24 | Tags: , ,

Comentar


(Vai mostrar seu Gravatar)

  Country flag

biuquote
  • Comentário
  • Pré-visualização
Loading



Quem é Giovanni Bassi

Giovanni Bassi Sou uma pessoa apaixonada por tecnologia e especificamente por .Net. Sou consultor independente especialista em .Net, focado em arquitetura e melhores práticas. Tenho dezenas de artigos publicados na .Net Magazine, revista da qual sou editor técnico. Ministro palestras e cursos de vez em quando, e quando dá tempo eu respiro um pouco. Mais detalhes nesta página.

Busca

Selos

Eu vou ao TechEd Brasil 2010, e você?

MVP

MCPD

MCSD

.Net Magazine

Abaixo ao if!

Calendário

«  julho 2010  »
seteququsedo
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
Ver detalhamento de posts no calendário

Blogs interessantes

    OPMLDownload OPML file

    Postagens recentes

    Comentários recentes

    Disclaimer / Aviso
    As opiniões colocadas neste blog são minhas e pessoais e não expressam necessariamente as opiniões de meus empregadores, pareceiros e amigos. Da mesma forma, os comentários feitos por leitores do blog não expressam a minha opinião.

    © Copyright 2010 .Net Unplugged
    Log in