REFACTORING – Caso Real e boas maneiras Parte IV (Keep Walking…)

Essa semana tive que resolver um bug na feature dos posts anteriores. Acabei esquecendo um caso que caia em uma Exception, mas não vamos tocar no assunto :).

O caso é o seguinte, como dito anteriormente, aconteceu que eu tive que manter esse código, corrigir a falha como dito anteriormente e porque não refatorar mais um pouco?

E o caso da refactoring de hoje, tem um tema central que é o uso excessivo de getters e setters (aquele mesmo que você usa, não sabe o porque, e todo mundo acha que está certo. Amém.).

Um amigo meu defende: “porque usar todos os atributos privados, se tem um método get e set para cada. Usa todos os atributos públicos e pronto. Muito mais elegante”. Bem, eu concordo e não concordo. O uso excessivo de getters e setters, no qual eu já defendi como correto(#fail pra mim), é prejudicial. Mesmo se você não precise, e não saiba o porque, você vai criar um. As vezes por costume, ou o framework usado no seu projeto obrigue. Mas enfim, vamos usá-los quando preciso, respeitando o encapsulamento. Fundamento da OO muitas vezes esquecido.

Então, como fazer? Como diz o Guilherme Silveira na palestra sobre design de código. No meu código, eu tenho um objeto que sempre terá um atributo. O que eu faço para que esse meu objeto sempre tenha esse atributo ao criá-lo? Colocar no construtor? Muito bem, é isso mesmo. Adicionando os atributos no construtor, muito dos setters serão inúteis, então só deletá-los.

Pois então vemos como ficou o nosso código. Focando no método carregar a lista de objetos para o relatório.

Repare em todos os setters que estão listados para “montar” o objeto, para então ser passado a lista. Repare que sempre eu passo mais um objeto do tipo ProdutividadeFisioterapeuta como parâmetro nos métodos, algo está errado. Enquanto na classe ProdutividadeFisioterapeuta.java está o clássico construtor sem parâmetros, chamando o método super() da classe Object. E repleta de getters e setters. Não vou listar a classe, mas acredite, eles estarão lá.

Pois então, vamos criar nosso construtor inteligente na classe ProdutividadeFisioterapeuta.java.

Perceba a clareza que ficou o nosso construtor em relação a bagunça anterior.

Vamos ver como ficou o método da listagem 1, após a refatoração.

Indo pro histórico do github, vemos ai o resultado. 42 linhas a menos e mais clareza no código.

E claro ia esquecendo do mais importante, retirar os setters inúteis. Após excluir os setters, substituí em outros locais do código onde eu os utilizava, como nos testes de integração da classe. Após o último commit:

Finalizando, como tenho dito, sempre você vai ter que manter seu código e se possível sempre tente melhorá-lo. Talvez um dia você olhe para o código e não encontre nada para melhorá-lo. Bem, eu nunca tive esse privilégio. Então é isso, mais um caso prático de como melhorar seu código. Excelente dica: Como não aprender OO com java. Excelente post da caelum.

Até mais e bons estudos.

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.

REFACTORING – Caso Real e Boas Maneiras: Parte II (Bad Smells)

Para começar, não vou citar uma receita de bolo, mas esses três passos já são um começo para que você refatore sem medo.

I)              Entenda sua regra de negócio: Pelo o que tenho notado, essa etapa não é dita na maioria dos tutoriais e livros, mas pra mim é de suma importância. Como você vai implementar algo em que não conhece ou o conceito não esteja “no sangue”? Bem, o Diniz fala que não tem problema, apenas ele vai levar um tempo maior para resolver o problema. Enfim, eu acho uma etapa importante, pois o raciocínio fluirá mais facilmente e não será preciso muitas voltas para resolver o problema.

II)            Crie testes automatizados para o seu código legado.

III)          Se for necessário criar algo novo,  use TDD.

IV)          REFATORE!

Então, temos os passos, vamos entender nosso problema.

I) Regra de Negócio. O código foi retirado de um sistema que já está em produção, portando é um código verídico(sim, eu consegui fazer!). Eu devia popular um objeto para ele ser repassado para um relatório que calcularia a produtividade de um funcionário. Basicamente é isso. E um dos campos seria preenchido com o valor  de horas da carga horária desse funcionário no intervalo escolhido pelo usuário.

 

Explicando de novo: Eu tenho uma referência de tempo fixa, escolhida pelo usuário que emitiria o relatório. A partir daí eu tenho um intervalo que será considerado, intervalo esse que será calculado a partir do período que é a carga horaria do funcionário. Ufa! Acho que deu pra entender.

Listagem 1.

Kent Back, fala sobre bad smells no seu código referindo a qualquer perda de legibilidade e design no seu código. De cara podemos citar vários. Listaremos alguns mais relevantes. E no próximo tópico, citarei práticas para corrigir esses “fedores”.

Eu lembro que quando revisaram esse código, eu ouvi “nossa mãe do céu!”. Pensei então: “a casa caiu”. E realmente esse não foi uma das minhas melhores performances.

Tá, mas o que diabos é essa cascata de ifs? Somente esse erro, ja desencadeia várias más praticas(e essa ganha de goleada.).  Com isso, vemos a lógica replicada 7 vezes , uma para cada caso que a carga horaria se encaixa no período referencia. Vemos a padronização dos parâmetros em que cada método recebe. São exatamente os mesmos, mudando apenas o comportamento interno entre eles.

Com isso temos um método com um tamanho grande, e na listagem 2, vemos a implementação dos 7 métodos, responsáveis pelos cálculos. E na listagem 3, vemos os métodos verificadores na classe CargaHoraria.java. Da mesma forma, também replicados. Juntando métodos replicados, com um método central que invoca os demais, temos como um resultado uma classe quilométrica. E quem gosta de manter uma classe desse tipo? Imagine que o cálculo precisa de uma pequena modificação. Pois é, temos que corrigir os 7 métodos.

Listagem 2

Dando continuidade, o Guilherme Silveira diz na sua palestra sobre Design de Código, que qualquer código que não caiba na resolução normal do monitor não merece respeito. E exatamente isso acontece, vemos nomes de métodos talvez explicativos até demais, tornando excessivamente extenso. Esses erros ocorrem na listagem 1 e 2.  Como por exemplo o método  DataUtil.getQuantidadeVezesOsDiasDaSemanaApareceramNoPeriodo(produtividadeDTO.getDtInicial(), produtividadeDTO.getDtFinal(), parseList(cargaHoraria, diasList)).doubleValue();

Listagem 3

Aqui merece um sono WTF! Que o Martin cita no inicio do livro clean code. E além de tudo, o método doubleValue() ainda é chamado pra tornar ainda mais feio. Mas calma, posteriormente mostrarei soluções.

II) Testes Automatizados: Já escrevi uma série de posts sobre testes(links), e pela web está repleta de material sobre o assunto. Então, qual garantia que eu tenho de alterar meu código, e ao final da refatoração ele está ainda fazendo o que eu acho que ele deve fazer? Testes baby! Abuse dos testes e verifique se o código que irá ser refatorado possui testes cobrindo as funcionalidades.

III) E então eu preciso adicionar uma funcionalidade nova. Ai que está o problema. Como eu vou fazer isso? Para garantir faça TDD. A Web também está repleta de material sobre o assunto. Pelo menos para você ganhar embasamento teórico. Pois, da teoria para a prática a distância é muito grande. E para você ter um aproveitamento maior, faça Pair Programming, prática do XP.  Foi a partir de um PP que a idéia desse post saiu. E minha maior sorte o me pair era bem mais experiente que eu, então o aprendizado foi muito grande. Recomendo essa técnica.

IV) Fica para o próximo post, onde apontarei soluções.

Link para a parte 1.

REFACTORING – Caso Real e boas maneiras Parte I (Intro)

1)        INTRO

A Engenharia de Software tradicional, ou pelo menos eu a chamo assim(aquela em que eu dormi muito nas aulas da faculdade), possui toda a uma divisão impecável de processos de software, quase todos aparentado uma linha de montagem do século XIX. Sendo alguns modelos mais conhecidos do que outros. Citando o modelo waterfall, vemos bem evidente a linha de montagem. Cada etapa do modelo, projeto, desenvolvimento, testes, etc. Ocorria ao término da anterior, sempre ocorrendo essa dependência.

Pois bem, os tempos mudaram, e com eles as novas metodologias de desenvolvimento de software despontaram no mercado.  Com todo “jeitão” interativo-incremental de ser. Afim de combater os modelos engessados citados anteriormente.

Mas o post não tem como objetivo discutir as vantagens de uma ou de outra, desvantagens e assim por diante.  Muito menos ter aquelas discussões filosóficas (métodos ágeis #win e pronto! :D ). E sim trazer uma preocupação que com o advento desses novos métodos fica muito mais evidente, que é a manutenção do código.

Não temos mais etapas bem definidas no desenvolvimento, muito menos naquele ciclo de vida do software. Aquele que diz que 70% do tempo de vida do seu software irá ser dedicado a manutenção.  Óbvio que não faço idéia de porcentagem exata. Mas veja bem, a cada momento que você revisa um código, feito por você ou não, a qualquer momento e em qualquer Sprint, mil perdãos amigos, mas vocês estão dando manutenção. E com essa tarefa cada vez mais em evidência, vemos a importância de tratarmos bem esse código, afinal de contas será você, seu colega de trabalho ou algum conhecido que irá encará-lo posteriormente e NÃO um  DAMMIT FREAK  COMPUTER .

Faremos algumas técnicas de Refactoring, em um código ruim(feito por mim), identificaremos “bad smells” e corrigiremos passo a passo, seguindo duas principais fontes. Refactoring – Improving The Design Of Existing Code, Martin Fowler e Clean Code: A Handbook of Agile Software Craftsmanship, Robert Martin.

E claro agradecer meu amigo @marceloemanoel, que sem ele, esse post não sairia.

 

2)         Refactoring

O termo refactoring possui muitas definições. Mas segundo Martin Fowler: Refatoração é uma mudança feita na estrutura interna do software para torná-lo mais fácil de entender e mais barato realizar qualquer mudança sem modificar o comportamento externo do sistema. Além de ser uma fonte chave no post, achei essa a melhor definição.  Bem ao final do post desejo ter demonstrado algumas práticas.

2.1) Porque refatorar?

Para responder essa perguntar vou ser bastante sucinto.  Você refatorando, seu código irá ter um ganho no design. Tendo um melhor design, o código fica mais fácil de ser entendido por GENTE. Sendo mais fácil de ser entendido, os bugs serão mais difíceis de aparecer, e os que já estão “perturbando”, serão achados mais facilmente.

Enfim, citei só o caminho feliz, pois eu creio que essa prática vale realmente a pena. Mas é necessário experiência e conhecimentos principalmente em Orientação a Objeto para chegar a um bom “Refactorer” J.

2.2) Quando refatorar?

Basicamente, sempre que o valor estético do seu código estiver em déficit, prejudicando a clareza e a comunicação.

Mas citando alguns casos aparecem em comum em ambos autores:

  • Código duplicado (duplicated code)
  • Método longo (long method)
  • Classe grande (large class)
  • Lista de parâmetros longa (long parameter list)
  • Má indentação(Bad Indentation)

Para mais detalhes, http://sourcemaking.com/refactoring possui uma síntese dos casos de refatoração extraídos do livro do Fowler.

Próxima etapa, colocaremos a mão na massa, veremos o código problemático e apontaremos os bad smells mais evidentes.

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.