[Refatorando Tudo!] Diga, não pergunte!

Esse são objetos burros, não devem ter nenhuma lógica

Ah… quantas vezes eu já ouvi isso… Geralmente esse argumento é reforçado por termos como beans ou POJOs (ou o equivalente da linguagem que você utiliza).

Voltando um pouco nos conceitos de Orientação a Objetos, a ideia básica é esconder os dados e expor comportamento. Assim, os objetos conseguem se comunicar e manter seu estado interno, o que facilitaria a manutenção do código.

Então ter “objetos burros” não só vai totalmente contra os princípios da orientação a objetos como também empurra lógica para classes onde elas não fazem sentido.

Dados andam junto com a lógica que precisa deles

Para exemplificar essa situação vamos exibir os detalhes de um pedido, como valor total, forma de pagamento utilizada, endereço de entrega etc. A informação de um pedido vem de um serviço, que retorna um JSON com todos os dados.

Utilizando um biblioteca que para mapear JSON para um objeto, como Jackson por exemplo, criamos a seguinte classe:
(para reduzir a quantidade de código focamos apenas no método de pagamento utilizado na compra)

public class Pedido {
// Algumas propriedades...
  @JsonProperty("metodo_pagamento")
  private MetodoDePagamento metodoDePagamento;
// Outras propriedades...
}

public class MetodoDePagamento {
// Algumas propriedades...
  @JsonProperty("cartao")
  private CartaoDeCredito cartaoDeCredito;
}

public class CartaoDeCredito {
  @JsonProperty("bandeira")
  private String bandeira;

  @JsonProperty("numero")
  private String numero;
}

Perfeito, agora para exibir as informações do cartão de créditos nós precisamos esconder uma parte dos dados e exibir apenas os 4 primeiros e 4 últimos dígitos.

Não vamos simplesmente adicionar essa lógica na view, certo? Mas o objeto que faz o mapeamento do JSON deve ser um “objeto burro” então a lógica também não pode ficar lá, apesar de fazer todo sentido. Então surgem os “utils”.

public class CartaoDeCreditoUtil {
  public static String formatarNumero(String numeroCartaoDeCredito) {
    return numeroCartaoDeCredito.substring(0, 3) +
              "-xxxx-xxxx-" + numeroCartaoDeCredito.substring(11);
  }
}

A chamada desse código lá na view seria algo assim:

Pedido pedido = servicoPedido.buscaDetalhes(numeroDoPedido)
//...
CartaoDeCreditoUtil.formatarNumero(
  pedido.getMetodoDePagamento().getCartaoDeCredito().getNumero())

Em um time pequeno pode até ser que todos saibam que devem utilizar essa classe para formatar o número do cartão de crédito. Mas imagine isso em um software que é mantido por anos, qual a probabilidade de alguém que chegou agora para dar manutenção saber que a lógica para formatar o cartão de crédito não está junto com o número do cartão? Qual a probabilidade dessa pessoa criar outra classe para manter essa lógica?

Diga ao objeto o que fazer

O exemplo acima é bem simples, mas já mostra os problemas de separar os dados do comportamento de um objeto. Mas e agora, como sair dessa situação?

O primeiro passo é encontrar a fonte de dado para os famigerados métodos da class “util”. Dependendo do seu contexto a refatoração pode ser mais complicada. No exemplo acima podemos simplesmente mover o método para dentro da classe CartaoDeCredito e criar um método getNumeroFormatado:

public class CartaoDeCredito {
// código anterior
  public String getNumeroFormatado() {
    return numero.substring(0, 3) + "-xxxx-xxxx-" + numero.substring(11);
  }
}

No entanto caso o método util dependa de dados que venham de objetos diferentes, pode ficar mais complicado decidir qual deles deve assumir a responsabilidade.

Outra situação complicada é se, apesar de ser um mesmo dado, eles vierem de lugares diferentes. Considerando o exemplo anterior, imagine que mais de uma classe tem um número de cartão de crédito, onde colocar essa lógica?

A opção mais óbvia é duplicar o código, embora ela também vá causar mais problemas. Se a sua linguagem possui alguma forma de mixin, como traits em Scala ou modules em Ruby, é possível extrair esse comportamento e adicioná-lo aonde for necessário.

Uma outra opção também é fazer com esse número de cartão de crédito seja mais do que apenas uma simples String, então poderemos adicionar o comportamento nele e utilizá-lo aonde for necessário lidar com o número do cartão.

Se você quer ir mais afundo no assunto, sugiro o post TellDontAsk de Martin Fowler e o Tell, Don’t Ask da Prag Prog. Esse último artigo vai um pouco além e também fala de outros princípios, que serão detalhados aqui também em outros posts, a Lei de Demeter e a separação entre Comando e Busca (Command/Query).

Compartilhe o texto com seus colegas para ajudá-los a melhorar o código deles também e continue acompanhando os posts da série [Refatorando tudo!] para melhorar ainda mais seu código!

Deixe seus comentário aqui no post ou fale direto comigo no twitter em @marcosbrizeno!

Anúncios

4 comentários sobre “[Refatorando Tudo!] Diga, não pergunte!

  1. celsosantarosa

    Muito bom o post, Brizeno. Isso é uma coisa que eu vivo pensando já faz um tempo. Entretanto, no exemplo do cartão de crédito, sinceramente eu penso que isso é lógica da apresentação e não do objeto (comportamento). Nesse caso em específico, creio que seja um bom uso de uma outra classe que trata número de cartão e o apresenta da maneira esperada.

    • Valeu Celso! O exemplo é realmente bem simples e não dá pra explorar tão bem, mas eu concordo que em alguns casos faz sentido separar o dado da maneira como ele é apresentado, principalmente quando a apresentação muda com mais frequência do que o objeto em si. Mas ainda assim é preciso tomar cuidado pra não expor demais o estado interno do objeto e complicar a manutenção.

  2. Leandro Miglioli

    Parabéns pelo seu post !!! Sou seu fã !!!!!

Deixe um comentário

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