“Maus cheiros” no código

Roger Bernardo de Melo Lima
6 min readAug 24, 2021

Antes de discutirmos sobre os code smells, termo criado por Kent Beck, temos de dissertar sobre o que é refatoração.

O termo “refatoração”, nada mais é que uma modificação feita na estrutura interna do software para deixá-lo mais legível e menos custoso para alterar, sem que seu comportamento observável seja alterado.

Com a refatoração melhoramos os design do software, deixamos todo o código mais fácil de se ler e entender, encontrar bugs e a ganhar tempo com a introdução de novas funcionalidades.

Agora temos uma boa introdução sobre como a refatoração funciona, mas saber como ela funciona não significa que sabemos quando fazê-la. Decidir quando começar e quando parar é tão importante quanto saber lidar com seu modo de funcionamento.

No livro “Refatoração: Aperfeiçoando o design de códigos existentes”, Martin Fowler e Kent Beck fornecem pistas de quando devemos olhar com mais carinho a necessidade de refatoração em uma porção de código.

Essas pistas são o que chamam “maus cheiros” no código. Esses cheiros apontam quando um código não deveria estar escrito e arquitetado daquela maneira e poderia ser benéfico uma refatoração. Vamos ver em seguida alguns deles.

01. Nome misterioso

Um bom código precisa ser claro e autoexplicativo. Então se há um nome no código, seja de variável, função ou qualquer outro tipo de contexto, que não esteja adequado e facilmente entendível, é certamente um problema.

Quando você não é capaz de pensar em um bom nome para algo, em geral é sinal de que há um problema mais profundo no design.

02. Código duplicado

Se houver a mesma estrutura de código repetida em mais de um lugar, com certeza há uma forma de unificar essas estruturas. Uma duplicação sempre significa que se houver a necessidade de uma mudança, há de ser vasculhado, em todo o código, as duplicatas.

03. Funções longas

Quanto mais longa uma função, maior a probabilidade dela estar com muitas responsabilidades. É difícil de testar, de ler e de dar manutenção. A regra geral é clara: quanto menores são as funções, melhores são.

04. Lista longa de parâmetros

Listas de parâmetros extensas são confusas por si só. Se for o caso de haver a necessidade, considere o uso de objetos por parâmetros, classes, consultas ou qualquer outro método de refatoração aplicada a esse contexto.

05. Dados globais

O problema dos dados globais é que eles podem ser modificados em qualquer ponto da aplicação, gerando assim uma alta probabilidade de criação de bugs difíceis de se rastrear. Caso se necessite do uso deles, que não sejam mutáveis e que haja algum tipo de encapsulamento para acesso à eles.

06. Dados mutáveis

Mudanças em dados muitas vezes resultam em consequências inesperadas e bugs. Então, quando menos se usa a mutabilidade de variáveis em uma base de código, menor a quantidade de bugs. Por isso é melhor e mais recomendado analisar as vantagens da imutabilidade; retornar dados novos em vez de modificar os antigos.

07. Alteração divergente

Nosso software deve permitir alterações com facilidade, é por isso que tem o soft no nome. Quando fazemos uma alteração, queremos ser capazes de acessar diretamente um único ponto claro no sistema e fazer essa alteração. Qualquer coisa diferente disso é um sinal de que algo está errado com nosso código. Ela ocorre quando um módulo é frequentemente alterado de formas diferentes por motivos diferentes.

08. Cirurgia com rifle

É parecida com a alteração divergente, porém é o oposto. Você sentirá quando, sempre que fizer uma alteração, tiver de fazer várias modificações pequenas em classes diferentes. Quando as alterações estão por todo lugar, é fácil esquecer de uma modificação importante.

09. Inveja de recursos

Ocorre quando, por exemplo, uma função de um módulo passa mais tempo se comunicando com funções ou dados de outro módulo do que com o seu próprio.

10. Agrupamento de dados

Dados são como crianças: adoram estar juntos! Quando há os mesmos dados, juntos, em vários lugares diferentes, existe ali um objeto implorando para nascer.

11. Obsessão pro primitivos

Percebe-se que em muitos ambientes, os programadores relutam em criar seus próprios tipos de dados específicos para aquele contexto. Deixam de criar tipos diversos como moeda, coordenadas, intervalos para tratar tudo como Strings ou números. Dados primitivos que tenham um determinado contexto, não devem ser, friamente, tratados como primitivos.

12. Switches repetidos

Um switch/case ou uma cascata de instruções if/else que são duplicados e estão em lugares diferentes são sérios candidatos à evoluírem para a implementação do polimorfismo.

13. Laços

Martin Fowler acredita que as operações de pipeline, como filtrar e mapear, ajudam a ver mais rapidamente os elementos incluídos em um processo e o que acontece com eles.

14. Elemento ocioso

Todo código cuja estrutura não seja mais necessária, deve deixar de existir.

15. Generalidade especulativa

Os programadores em geral tentam prever o futuro de um método ou classe imaginando que, por um acaso, eles seriam utilizados mais vezes e em diversos contextos diferentes. Por conta disso, adicionam todo tipo de ganchos, ou casos especiais que não são necessários e que por vezes, nunca são utilizados. Tudo aquilo que não tiver uso real, deve ser eliminado.

16. Campo temporário

Às vezes, vemos, em uma classe, um campo que é definido somente em determinadas circunstâncias. Esse tipo de código é difícil de manter e dar manutenção ao tentar entender o porquê desse campo existir ou não existir em um contexto.

17. Cadeia de mensagens

Vemos cadeias de mensagens quando um cliente pede um objeto para outro objeto, cujo cliente então pede para outro objeto e assim se segue ad infinitum. Navegar dessa forma significa que o cliente está acoplado à estrutura de navegação. Qualquer mudança nesses objetos intermediários, obriga o cliente a ter de mudar.

18. Intermediário

Imagine o exemplo de uma classe A que para se comunicar com a classe B, precisa da classe C para intermédio da comunicação. Esse exemplo é bobo, mas isso pode ir muito mais longe. De modo geral, quanto menos intermediários, melhor.

19. Trocas escusas

Módulos diferentes devem trocar o mínimo de informação possível, e se precisarem trocar, precisam ser de forma clara e limpa.

20. Classes grandes

Normalmente uma classe grande existe por excesso de responsabilidades, e quando isso acontece, código duplicado ou mesmo deslocado não deve estar muito distante.

21. Classes alternativas com interfaces diferentes

Uma das grandes vantagens do uso de classes é o suporte à substituição, permitindo que uma classe seja usada no lugar da outra conforme necessidade. Porém isso só funciona quando as interfaces das classes são iguais.

22. Classes de dados

Essas classes são armazenadores de dados burro: têm campos, métodos de leitura e escrita (getters e setters) e nada mais. E ainda por cima, são usadas de forma extremamente detalhada por outras classes.

23. Herança recusada

As subclasses herdam os métodos e os dados de seus pais. A ideia da herança é que os filhos de uma classe usem tudo o que receberem de seus pais. Caso algum filho não queira e nem precise utilizar aquilo que recebe de seu pai, é porque a hierarquia está incorreta.

24. Comentários

Se há a necessidade de comentar o código para explicá-lo, as chances desse código merecer uma refatoração são altíssimas. Após esse processo, percebe-se que os comentários são supérfluos.

Comentários devem ser usados com moderação e em casos especiais — explicando o contexto daquela decisão técnica, por exemplo — não para explicar códigos feitos no Go Horse.

Referências

CODE Smells. [S. l.], 23 ago. 2021. Disponível em: https://refactoring.guru/pt-br/refactoring/smells. Acesso em: 23 ago. 2021.

FOWLER, Martin. “Maus cheiros” no código. In: REFATORAÇÃO: Aperfeiçoando o design de códigos existentes. 2. ed. [S. l.: s. n.], 2019. cap. 3.

--

--