REFACTORING – Caso Real e boas maneiras Parte III (Soluções)

Bem, citamos várias falhas no post anterior. Vamos tentar resolver a replicação da lógica de negócio. Primeiro nossa classe ProdutividadeService.java possui outras responsabilidades além de realizar esses cálculos. Assim ferindo um princípio básico na Orientação a Objeto, o SRP(link) (Single Responsability Principle). Vamos levar toda essa lógica para uma classe apropriada que tenha apenas essa missão.

Coesão ++.

Então, toda essa regra se trata de calcular um intervalo de tempo que irá ser considerado no meu relatório. Então criamos a classe Intervalo.java, onde as regras serão centralizadas. Estamos criando algo novo, então estamos utilizando TDD. A seguir um exemplo de teste implementado na classe IntervaloTest.java.

Listagem 1. Exemplo de método de teste

Claro, para chegar nesse nível de legibilidade não foi de repente. Escrevemos códigos ruins, e melhoramos gradativamente. Todos os passos de refatoração de sua classe vale para testes. Mais pra frente, mostrarei uma boa prática na escrita de testes.

Ao final, temos como resultado a classe Intervalo.java:

Listagem 2.

O método intersecao() será aquele utilizado para calcular o intervalo em si. Para cada if mostrado anteriormente temos um booleano que representa os casos que devem ser levados em conta, consequentemente, os casos abordados pelos testes. Para cada condicional atendido, um novo intervalo será retornado levando em conta a dataInicial e dataFinal coerentes.

Como curiosidade, vemos que com a utilização do TDD, um caso novo foi incluído(o caso de um intervalo não interceptar o outro. Por exemplo, o intervalo usado como referencia for no período de junho e o outro intervalo está em um mês diferente. Retornamos null), e mesmo assim temos menos ifs explicitando cada caso e mesmo assim os testes são atendidos.Dessa forma excluímos código desnecessário, diminuindo ainda mais o número de linhas sem prejudicar no entendimento.

Também não criamos os famosos getters e setters  para cada atributo da classe. Bem, os getters foram utilizados, mas os setters não tivemos necessidade graças ao construtor inteligente da classe.

Encapsulamento ++.

Mas onde a classe intervalo sera usada? Adicionamos na classe CargaHorária.java e Produtividade.java, eles terão um Intervalo (composição).

Todos os métodos replicados serão excluídos:

Listagem 3. Resultado na classe CargaHoraria.java

Método calculaHoras foi movido para a classe CargaHoraria, trazendo a responsabilidade para uma entidade mais apropriada. Mas ainda vemos o que melhorar, como a extensão dos nomes, os métodos doubleValue(), a extensão das listas de parâmentros, mas ainda não acabamos.

Listagem 4. Método calculaHoras.. na classe ProdutividadeService depois da refatoração:

Todo aquele código foi excluído, restando apenas esse trecho. Lindo!

Tirei um print screen com o log do github:

Isso mesmo, 102 linhas de código inúteis a menos, que agora não mais atormentarão. E o sistema ainda fazendo o que deve fazer com testes cobrindo qualquer modificação minha.

A primeira etapa de refatoração concluída, vemos que ganhamos bastante, código desnecessário foi deletado, regra de negócio centralizada, a feiosa cascata de ifs também excluída, algumas renomeações feitas. Mas há ainda o que melhorar.

A utilização de métodos estáticos pra mim soa estranho, quase a OO tem um derrame ao utilizer esses métodos ditos genéricos, que nós criamos. Para isso vamos extrair o método de calculo de Dias em um Período, da classe DataUtil.java, para a classeIntervalo.java.


Double numeroDeDias = intersecao.getQuantidadeVezesOsDiasDaSemanaApareceramNoPeriodo(capturaDiasDaSemana()).doubleValue()

Integer aparicoesNoIntervalo(List<DiaDaSemana> dias);

Ok, mas dando uma olhada no método calculaHoras() na classe CargaHorária.java,  se eu tenho duas propriedades da classe interseção passadas como parâmetro, porque não levar esse método para a classe Interseção. Já resolve o problema da longa lista de parâmetros, prejudicial na legibilidade do método. Além do método capturaDias(), além de não dizer nada, percebeu que ele era inútil, olhe que maravilha, mais código desnecessário excluído.

Listagem 5. Método calculaHoras.. na classe ProdutividadeService.java

Mais algumas renomeações e extrações, percebemos que se tornaria ainda mais legível, levar o método calculaHoras() para Produtividade.java. A responsabilidade de calcular horas realmente deve ser dessa entidade. Um simples cast resolveu aqueles métodos doubleValue().

Design ++.

E por fim, não basta refatorar a classe, os testes terão que ser mantidos. Uma olhada nessa bizarrice feita por mim.

Nunca é legal você entupir os métodos com asserts. Reduza o número deles, assim você diminui a responsabilidade de cada método de teste e modularize seus testes, cada um testando determinada unidade de código.

Mas como fazer? Ai aparece uma prática simples, em que eu particularmente dava valor: Sobrescreva o equals() e o hashCode(). Olha que maravilha. Ao invés de criar uma assertiva para cada atributo do objeto. Crie um objeto com os resultados esperados. Assim apenas um assert é criado, realizando todos os comparativos feitos acima. Código a seguir:

OBS: Pra quem estreanhou, eu estar falando de Funcionário e depois apareceu Fisioterapeuta.Mude apenas a nomenclatura. Eu estava tentando generalizar os exemplos mas espero que não tenha prejudicado o entendimento. Qualquer coisa mandem feedbacks.

Link para a parte I.

Link para a parte II.

Abraço pessoal. Bons estudos e espero ter compartilhado uma experiência bacana.

2 Responses to REFACTORING – Caso Real e boas maneiras Parte III (Soluções)

  1. Renan Gurgel disse:

    Achei irado Yuri, parabéns pelo post! Ele é no mínimo animador para aqueles (assim como eu) ainda não utilizam TDD e não fazem constantes refatorações no código. Fica a sugestão de próximo post um sobre TDD puro limpo e seco! Vlw!

Deixe uma resposta

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

%d blogueiros gostam disto: