Aprendendo TDD! Escrevendo testes unitários

Você provavelmente já jogou o jogo da velha antes, então deve conhecer bem as regras e condições de vitórias. Então vamos usar esse conhecimento para exercitar a escrita de testes!

Testando entrada e saída

Vamos começar com um exemplo simples, queremos testar a entrada e saída do quadro do jogo da velha. Para isso crie um diretório para nosso projetinho e dentro dele adicione uma pasta lib e outra pasta test.

Vamos começar criando o quadro, ele vai receber o caractere do jogador e a posição x e y no qual a jogada foi feita. Para isso coloque o código a seguir dentro do arquivo lib/quadro.rb:

class Quadro
  attr_reader :posicoes

  def initialize()
    @posicoes = []
  end
def executar_jogada(caractere, posicao_x, posicao_y)
    @posicoes[posicao_x] ||= []
    @posicoes[posicao_x][posicao_y] = caractere
  end
end

Para testar esse método, podemos criar uma nova instância de um quadro, chamar o método para fazer a jogada e ao fim verificar se a posição passada foi preenchida com o caractere passado.

Crie os seguintes testes no arquivo test/quadro_test.rb:

require 'minitest/autorun'
require 'minitest/spec'
require_relative '../lib/quadro.rb'

describe Quadro do
  it "realiza jogada na posicao especificada" do
    quadro = Quadro.new
    quadro.executar_jogada('x', 1, 1)
    quadro.posicoes[1][1].must_equal 'x'
  end
end

Para executar os nossos testes, vá para a raiz do diretório e execute:


ruby test/quadro_test.rb

O que tem em um teste

Apesar de simples, o exemplo acima demonstra muito bem o esqueleto que todo teste segue: 1) preparar os dados 2) executar a ação e 3) verificar os resultados.

Ao preparar os dados buscamos garantir tudo o que é necessário para o teste ser executado, sem depender de mais nada. Aqui vamos instanciar novos objetos, configurar dublês (mocks) e qualquer outra informação para exercitar o código.

Na etapa de ação vamos executar o código sob testes, utilizando o mínimo de código possível. É importante que a execução seja apenas uma linha pois queremos que os testes sejam focados em apenas uma ação, assim ao falharem sabemos o motivo.

Na última parte, queremos garantir que os resultados esperados com a ação foram obtidos. Além disso, caso o teste não passe, também temos que deixar claro o que causou a falha.

O que testar?

No exemplo anterior fizemos o conhecido “caminho feliz” que é um cenário onde não consideramos erros ou problemas que possam acontecer. O aconteceria caso o jogador escolha uma posição fora do quadro?

Agora que sabemos como o código funciona podemos escrever o teste primeiro, garantindo que uma exceção será lançada ao fazer uma jogada fora do quadro.

describe Quadro do
  it "lanca excecao se a jogada for numa posicao invalida" do
    quadro = Quadro.new
    assert_raises JogadaInvalidaException do
      quadro.executar_jogada('x', 3, 3)
    end
  end
end

Agora podemos implementar o código que fará com que esse teste passe. Basta validar a posição x e y passadas como entrada e lançar a exceção caso ela seja inválida.

class Quadro
  attr_reader :posicoes

  def initialize()
    @posicoes = []
  end
def executar_jogada(caractere, posicao_x, posicao_y)
    raise JogadaInvalidaException if posicao_x >= 3 || posicao_y >= 3
@posicoes[posicao_x] ||= [] @posicoes[posicao_x][posicao_y] = caractere
end
end

Com isso garantimos não só que o nosso código funciona como esperado, mas também que outros desenvolvedores tenham uma especificação detalhada de como ele funciona. Quem lê os testes sabe que deve tratar a exceção e quando ela será lançada.

Agora, pense em quais outros cenários podemos cobrir. Consegue ver outros cenários de jogadas inválidas? Tente escrever os testes primeiro e depois a implementação.

Nos próximos posts vamos abordar alguns aspectos mais avançados de testes e continuar construindo nosso jogo da velha guiando o desenvolvimento através de testes.

Se você está gostando da série Aprendendo TDD! compartilhe com amigos e colegas que também querem aprender a utilizar esta técnica. Tem alguma dúvida, sugestão ou comentário? Use o espaço no blog ou me procure no twitter em @marcosbrizeno.

Aprendendo TDD! Pra que testes?

“Nossa, o condicional desse if está muito grande e confuso…”, você pensa ao tentar entender a lógica de processamento de pagamento da aplicação. Até pensa em refatorar e extrair a lógica pra um método, mas ai lembra que essa parte é a que valida o pagamento. Melhor deixar quieto e não mexer no que funciona né?

Os benefícios de ter uma boa cobertura de testes, seja fazendo TDD ou o outro TDD (Teste Depois do Desenvolvimento) são muito bons. A confiança de validar o seu trabalho executando apenas um comando são muito grandes. Nós temos a sorte de poder contar com esta técnica na nossa profissão, então vamos usá-la?

TDD está vivo ou morto?

Algum tempo atrás tentaram matar o TDD, mas acho que ele sobreviveu no final das contas. E então, vale a pena fazer TDD? Eu acredito que sim e faço todo dia.

Fazer TDD é difícil e exige uma boa quantidade de prática e treino. Das primeiras vezes que tentei fazer TDD eu simplesmente não conseguia visualizar os testes e saber o que escrever. Então eu decidi focar em fazer testes, mesmo que escrevendo a lógica primeiro, até me acostumar com a estrutura de testes.

Hoje em dia eu consigo sentir que, ao pensar primeiro nos testes e em como eu gostaria de usar o código que vou escrever antes de escrevê-lo, eu consigo visualizar melhor o design e separar as responsabilidades. Mas isso levou um bom tempo, para quem ainda não tem o costume de escrever testes, talvez já começar com TDD seja um esforço muito grande que acabe atrapalhando os benefícios da técnica.

Então, antes de se preocupar em fazer os testes antes, foque em escrever testes e ouvir o feedback que eles te dão.

O que os testes te dizem

Testes são a melhor maneira de documentar seu código, pois eles mudam conforme a lógica. Uma API que possui testes validando o seu contrato, permite que qualquer pessoa veja os testes e entenda quais são os dados que precisam ser enviados.

Uma documentação estática, apesar de importante, exige um esforço extra para os desenvolvedores, não só de escrever, mas também de lembrar de atualizar a cada mudança no código. Então manter os testes legíveis é tão importante quanto manter o código de produção legível.

Ter testes diminui muito a carga cognitiva que precisamos carregar ao desenvolver, pois eles te dão feedback na hora sobre o que você fez. Se você esqueceu de alguma coisa, ou inverteu a lógica do if sem querer, os testes estão lá pra te salvar.

Além disso, o benefício mais óbvio dos testes é aumentar a confiança da equipe ao aplicar mudanças e fazer melhorias no código. Ao corrigir problemas e bugs no código é importante garantir que eles não voltem, adicionando mais testes.

Esse foi o primeiro post da série Aprendendo TDD! Fique ligado pois toda semana terá uma publicação nova com dicas para quem quer começar a escrever testes e aplicar TDD.

 

[Refatorando Tudo!] Não use parâmetros como retorno

Sabe quando você precisa encontrar aquele bug demoníaco e não faz a menor ideia de onde ele está? Ai você começa a debugar o código e põe break point em tudo que é lugar pra tentar entender o que está acontecendo. A pior coisa que você pode encontrar são métodos com efeitos colaterais.

Dano Colateral

Imagine que vamos processar a criação de um pedido de compras. Para isso precisamos calcular o valor do frete e verificar se o usuário fazendo a compra faz parte do programa premium da empresa para marcar o produto como entrega expressa. O código atual parece algo assim:

class PedidoController
  def criar_pedido
    pedido = Pedido.criar_pedido_do_carrinho
    usuario = Usuario.buscar_por_id(params.id_usuario)
    valor_frete = calcular_frete(pedido, usuario)
  end
# ...
  def calcular_frete(pedido, usuario)
    frete = pedido.valor_total > VALOR_COM_FRETE_GRATIS ?
              0 : pedido.valor_total * 0.10
    pedido.entrega_expressa = usuario.premium? && frete == 0
    transportadoras.enviar_aviso_entrega_expressa(pedido) if pedido.entrega_expressa?
    pedido.remover_produtos_estoque
    frete
  end

Vamos entender o que está acontecendo no método calcular_frete:

Primeiro o valor do frete é definido baseado no preço do pedido.

Depois marcamos o produto como entrega expressa e enviamos essa informação para as transportadoras, dependendo do valor do frete calculado antes.

Por fim os produtos do pedido são removidos do estoque e o valor do frete é retornado.

Bom, só pela explicação já deu pra perceber que esse método faz coisa demais né? Mas o principal problema, que provavelmente vai nos pegar no futuro são as alterações no objeto produto que acontecem dentro de um método chamado calcular_frete.

Imagine que a lógica para marcar o pedido como entrega expressa está errada e você precisa corrigi-lá. Não é intuitivo achar que no método calcular_frete o pedido é marcado como entrega expressa. Esse é o principal problema de métodos com efeitos colaterais.

Identificando qual anti-padrão estamos lidando

A regra mais óbvia para se seguir é:

Não altere objetos que são passados como parâmetro

Mas, uma vez que estamos nessa situação, como sair dela? Para isso precisamos entender o motivo de o método está alterando os parâmetros. Aqui vão algumas possibilidades:

Violando o Princípio da Responsabilidade Única

Se você não conhece o Princípio da Responsabilidade Única (Single Responsibility Principle), veja esse outro post que tem mais detalhes.

Como falado anteriormente, apesar de ser um exemplo simples, só pela explicação já fica claro a quantidade de coisas que o método calcular_frete faz é grande demais para um método só.

No entanto eu acho difícil seguir esse princípio pois definir o que é uma responsabilidade é muito subjetivo. Processar pagamento é uma responsabilidade? Ou será que buscar os produtos de um pedido deveria ser uma responsabilidade separada?

Tentando dividir as responsabilidades, podemos ter o seguinte cenário:

class PedidoController
  def criar_pedido
    pedido = Pedido.criar_pedido_do_carrinho
    usuario = Usuario.buscar_por_id(params.id_usuario)
    valor_frete = calcular_frete(pedido, usuario)
    marcar_pedido_expresso(pedido, usuario, valor_frete)
    enviar_aviso_entrega_expressa(pedido)
    pedido.remover_produtos_estoque
  end

  def calcular_frete(pedido, usuario)
    pedido.valor_total > VALOR_COM_FRETE_GRATIS ?
              0 : pedido.valor_total * 0.10
  end

  def marcar_pedido_expresso(pedido, usuario, frete)
    pedido.entrega_expressa = usuario.premium? && frete == 0
  end

  def enviar_aviso_entrega_expresa(pedido)
    transportadoras.enviar_aviso_entrega_expressa(pedido) if pedido.entrega_expressa?
  end
end

As responsabilidades estão bem separadas agora, mas será que realmente melhoramos o design da aplicação? Olhe o tamanho do método criar_pedido agora, ainda continuamos alterando um parâmetro no método marcar_pedido_expresso e será que esses outros métodos realmente fazem parte do PedidoController?

Ter o princípio em mente é uma boa maneira de guiar a refatoração, mas na prática vale mais a pena combiná-lo com outros princípios para decidir como refatorar o código.

Objetos com Inveja

O code smell Feature Envy mostra uma situações onde, para implementar certa funcionalidade, um objeto precisa consulta muito mais outros objetos do que a si mesmo.

Uma maneira bem fácil de perceber essa situação é olhar o método sem focar em uma linha específica. Volte ao código anterior e tente olhar todo o corpo do método buscando padrões e repetições.

Fica bem claro ver a quantidade de chamadas ao objeto pedido, seja para ler informações dele, passá-lo como parâmetro ou até mesmo modificá-lo. Esse é um sinal de que o PedidoController está com inveja das funcionalidades de Pedido.

Podemos refatorar o código movendo essas responsabilidades de PedidoController para Pedido:

class PedidoController
  def criar_pedido
    pedido = Pedido.criar_pedido_do_carrinho
    usuario = Usuario.buscar_por_id(params.id_usuario)
    valor_frete = calcular_frete(pedido, usuario)
    pedido.marcar_como_entrega_expressa(usuario, valor_frete)
    enviar_aviso_entrega_expressa(pedido)
    pedido.remover_produtos_estoque
  end

  def enviar_aviso_entrega_expresa(pedido)
    transportadoras.enviar_aviso_entrega_expressa(pedido) if pedido.entrega_expressa?
  end
end

class Pedido
  def marcar_como_entrega_expressa(usuario, frete)
    self.entrega_expressa = usuario.premium? && frete == 0
  end
end

Agora não temos mais o efeito colateral de marcar o pedido como entrega expressa em um lugar onde não fazia sentido. Caso essa lógica precise mudar ela está concentrada em um método dentro da classe Pedido.

Violando o Diga, não Pergunte!

No post anterior vimos o princípio Diga, não Pergunte, que tenta evitar que o comportamento dos objetos fique separado das informações.

Se o código consulta as informações de um objeto para decidir o que fazer com ele, é sinal de que o princípio está sendo violado. Podemos mover então a responsabilidade de falar com as transportadoras para a classe pedido, já que ela possui a informação necessária:

class PedidoController
  def criar_pedido
    pedido = Pedido.criar_pedido_do_carrinho
    usuario = Usuario.buscar_por_id(params.id_usuario)
    valor_frete = calcular_frete(pedido, usuario)
    pedido.marcar_como_entrega_expressa(usuario, valor_frete)
    pedido.enviar_aviso_entrega_expressa(transportadoras)
    pedido.remover_produtos_estoque
  end
end

class Pedido
  def marcar_como_entrega_expressa(usuario, frete)
    self.entrega_expressa = usuario.premium? && frete == 0
  end

  def enviar_aviso_entrega_expresa(transportadoras)
    transportadoras.enviar_aviso_entrega_expressa(self) if entrega_expressa?
  end
end

Mesmo após todas essas refatorações ainda continuamos com o método criar_pedido tendo muitas responsabilidades e acionando bastante o objeto pedido, apesar de que agora ele não o modifica diretamente.

Como poderíamos melhorar o design desse código? Pense um pouco em como você melhoraria esse código e deixa as suas ideias nos comentários abaixo.

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!

 

 

[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!

[Refatorando Tudo!] Representando nada com o Padrão de Projeto Null OBject

No post anterior nos falamos sobre a importância de não retornar nulos nas funções. Uma maneira de evitar esse problema é utilizando um objeto válido, mas que não possui nenhuma informação. Nesse post vamos entrar mais em detalhes sobre o Padrão Null Object.

Nada é alguma coisa

No post anterior já dei essa dica, mas pra quem perdeu, essa apresentação da Sandi Metz Nothing is Something é muito boa e explica as implicações de design de retornar nulo.

A ideia básica do padrão é criar um objeto que é válido, ou seja não vai lançar nenhuma exceção ou causar problemas, mas que não possui nenhuma informação.

class Pedido
  attr_reader :total, :imposto, :numero
  def initialize(total, imposto, numero)
    @total, @imposto, @numero = total, imposto, numero
  end
end

class PedidoInvalido < Pedido
  def initialize
    super(0, 0, 0)
  end
end

Mas qual a vantagem de retornar um novo objeto ao invés de retornar nulo, que já existe?

Se você programa em uma linguagem Orientada a Objetos, provavelmente conhece o conceito de que objetos trocam mensagens entre si. A principal vantagem de criar um objeto invalido é que ele segue a mesma interface do objeto original, enquanto um nulo não conhece nada sobre o objeto original. Assim podemos continuar enviando a mesma mensagem, sem se preocupar se o objeto vai entender ou não.

Então mesmo que pareça fazer sentido utilizar um valor nulo para representar a falta de algum dado, estamos obrigando quem utiliza nossa função a prover um comportamento extra. Em algum lugar do código eventualmente vamos precisar fazer algo assim:

pedidos = Pedido.obter_historico_de_pedidos(usuario)
valor_total = pedidos.reduce(0) do |total, pedido|
  total += pedido.nil? ? 0 : pedido.total
end

Aquele nil? no meio da sua lógica tende a se espalhar em todo lugar que precisa de um pedido. Então, a menos que o método obter_historico_de_pedidos retorne um objeto nulo, quando não houver um pedido, toda parte do código vai precisar lidar com a checagem de nulo.

Apesar de parecer um trabalho grande criar um novo objeto só para representar um valor inválido, a manutenção fica fácil pois tudo fica concentrado em um lugar só. E utilizar um objeto é bem mais fácil do que utilizar a lógica toda.

Cada caso é um caso

O que o objeto nulo vai fazer depende muito da sua aplicação. No post anterior discutimos quando faz mais sentido retornar um objeto nulo ou lançar uma exceção, se você ainda não viu, corre lá pra evitar utilizar o padrão em um cenário onde ele não faz sentido.

Em alguns contexto o objeto nulo precisa ser mais complexo, como por exemplo quando existem outro objetos aninhados e eventualmente precisamos criar objetos nulos para eles também.

No entanto o objeto nulo também pode ser mais simples, digamos que você tenha uma lista de objetos, basta criar um lista vazia para garantir que o objeto será válido mas sem nenhum dado.

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!

[Refatorando tudo!] Não retorne nulo. Nunca.

Considere um exemplo bem simples: precisamos exibir a lista com as últimas compras que um usuário fez.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) { ... }

Para complicar um pouco mais, imagine que existe um serviço cuja responsabilidade é gerenciar compras. Esse serviço expõe uma interface que retorna o histórico de compras de um usuário, dado o seu email.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    return null;
  }
}

Esse código funciona bem, mas tem um grande problema (que pelo título do post você já deve ter percebido): ele nem sempre retorna o histórico de compras.

Não me obrigue a saber de coisas!

Como Rob Looby explica nesse post fantástico, existem milhares de coisas que nós desenvolvedores precisamos manter na cabeça: a estrutura do programa, o novo design que queremos seguir, boas práticas do time, regras de negócio etc. Os possíveis retornos de uma função/método não deveriam ser uma dessas coisas.

Para quem escreveu o código acima, retornar nulo pode parecer como uma boa decisão. Não tenho nenhum histórico de compras para exibir, então não retorno nada. Como se representa nada? Com um valor nulo.

Se você também acha que null é a melhor maneira de representar nada, sugiro assistir a apresentação da Sandi Metz, Nothing is Something (na verdade assista a palestra independente do que você acha, vale muito a pena :D).

Na grande maioria das vezes, quando retornamos nulo na verdade não queremos retornar um nulo, mas sim dizer algo mais. No exemplo acima queremos dizer que não encontramos nenhum histórico! O que é bem diferente de nulo.

O grande problema é que ao definir uma função que retorna um valor especial, seja ele nulo, 0, -1 ou qualquer outro número mágico, estamos assumindo que quem utilizar esse código vai saber e lembrar disso sempre que usar a nossa função. Por exemplo:

[1,2,3].index(2)
# => 1
[1,2,3].index(5)
# => nil

Em ruby, ao chamar o método index com um valor inexistente, nil é retornado. Dependendo do contexto, pode até fazer sentido já que nil é considerado falso em uma checagem de booleano. Mas de novo, obrigamos a pessoa que utiliza o método a lembrar disso, sempre!

Nada é alguma coisa

Ao invés de retornar um valor especial, podemos ser mais explícitos e diminuir a responsabilidade de quem vai usar nosso código. Considerando o exemplo inicial, vamos imaginar agora dois cenários:

  1. Dado o email de um usuário, quando nenhum histórico for encontrado, então uma mensagem de erro deve ser exibida na tela.
  2. Dado o email de um usuário, quando nenhum histórico for encontrado, então nenhuma informação deve ser exibida.

No primeiro cenário podemos melhorar o nosso código utilizando uma exceção:

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    throw new HistoricoNaoEncontrado("Histórico inexistenete para o email: " + email);
  }
}

Assim, a pessoa que utilizar o código vai saber que precisa tratar a exceção HistoricoNaoEncontrado e, seguindo o cenário, vai exibir a mensagem de error quando ela for lançada.

Já no segundo cenário, quando basta não exibir nenhum informação na tela, podemos utilizar o Padrão de Projeto Null Object. Um Null Object é simplesmente uma representação de um objeto que não possui nenhuma informação, mas que também não retorna nulo ou lança exceções.

Por exemplo, supondo que a classe HistoricoDeCompras possua uma lista de Compras, podemos criar um Null Object para ela que possua o mesmo tipo, através de herança, e que tenha uma lista de Compras válida, mas vazia:

public class HistoricoDeCompras {
  private List<Compras> compras;
  ...
}

public class HistoricoDeComprasNulo extends HistoricoDeCompras {
  private List<Compras> compras = new ArrayList();
  ...
}

Assim, ao retornar o HistoricoDeComprasNulo, não precisamos obrigar a pessoa a lembrar nenhum detalhe de implementação, o código continua funcional.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    return new HistoricoDeComprasNulo();
  }
}

Em Scala, Java 8 e várias outras linguagens temos a nossa disposição valores opcionais. Com eles deixamos a cargo de quem utiliza o método lidar com a falta de informação, mas sem precisar retornar um valor mágico.

var helloWorld: Option[String] = Some("Hello World!")
println(helloWorld.getOrElse("Hello who?"))
// Hello World!
var helloWorld: Option[String] = None
println(helloWorld.getOrElse("Hello who?"))
// Hello who?

Valores opcionais permitem definir o que fazer quando o dado não está presente. No exemplo acima, em Scala, utilizamos Some para definir um valor válido e None para um valor inexistente. Ao utilizar o método getOrElse podemos definir o que será retornado caso nada seja encontrado.

Esse post do Rodrigo Turini explica com mais detalhes como utilizar valores opcionais no Java 8: http://blog.caelum.com.br/chega-de-nullpointerexception-trabalhe-com-o-java-util-optional/.

Pode parecer besteira ter essa trabalheira a mais só para não retornar null, mas acredite, o seu “eu do futuro” vai te agradecer se você parar de fazer isso!

Não se esqueça de conferir o links do post para se aprofundar mais no assunto.

Compartilhe o texto com seus colegas para evitar que o “eu do futuro” deles também sofra com esses problemas e continue acompanhando os posts da série [Refatorando tudo!] para melhorar ainda mais seu código!

Como você organiza seu código?

Muitas práticas de programação tem como objetivo principal facilitar a leitura do programa, uma delas é a organização de código. Toda linguagem tem alguma forma de empacotar e dividir a lógica de negócio, mas que ao final não fazem tanta diferença assim na execução do programa.

Apesar de parecer ser uma atividade simples, organizar o código é algo que eu nunca havia pensado com muitos detalhes antes, até que um amigo me apontou para um artigo que apresenta quatro estratégias para organizar seu código.

Na grande maioria dos códigos em que trabalhei, a divisão mais comum é a por tipo, que organiza os pacotes baseado na função que cada classe desempenha. Aplicações Rails por exemplo são automaticamente geradas seguindo esse paradigma.

Captura de Tela 2016-05-21 às 6.56.39 AM

Todos os modelos ficam em uma pasta dedicada, todos os arquivos de configuração ficam em outra. No entanto o que isso te diz sobre o domínio da sua aplicação? Será que a organização das pastas deveria te dizer algo sobre o domínio? Recomendo assistir a apresentação Architecture the Lost Years, de Bob Martin, para ver mais detalhes sobre esse ponto.

Não é que essa organização esteja errada, o programa continuará funcionando normalmente e desenvolvedores conseguem entender a arquitetura e navegar pelo o código. O problema é pensar que essa é a única maneira de organização!

Na maioria dos projetos com frameworks Java que já vi é comum encontrar divisão por pacotes que não agrega nenhuma informação lógica – mesmo não sendo frameworks com convenções tão forte como Rails.

Por exemplo, todas as classes ficam dentro do pacote br.com.empresa.nomeDoProjeto, o único conteúdo do pacote br é o pacote com, que também tem como único conteúdo o pacote empresa… até que, depois de vários pacotes “vazios” chegamos ao código real.

O problema da má organização de pacotes é que isso pode forçar a complicar mais os nomes das classes. Tanto que existe um jogo Java: Real or Not? onde precisamos escolher qual classe é real do framework Spring entre outras duas que são inventadas.

Mesmo em projetos que usam frameworks com forte convenção, como Rails, é possível adaptar o código para que ele seja organizado de uma maneira diferente, como mostrado no post Baking a Cake with Rails.

Se você prefere organizar pacotes por componentes, por camadas ou qualquer outra forma, é possível! O objetivo é tornar a arquitetura e design mais explícitos, não seguir cegamente um framework, como descrito no post Screaming Architecture.

Como falei no começo, organização de código é algo que só comecei a me preocupar de verdade recentemente, mas já pude ver que, ao utilizar uma abordagem de divisão por componente o código fica bem mais conciso e fácil de navegar.

E você, já tinha parado pra pensar em como organiza seu código antes? Qual sua maneira preferida?