Code Smells
O que é um smell
Seção intitulada “O que é um smell”O termo é de Kent Beck, popularizado no capítulo “Bad Smells in Code” de Fowler (“Refactoring”, 1ª ed., 1999; revisado na 2ª ed., 2018 — algumas notas usam os nomes clássicos da 1ª ed., como Long Method e Switch Statements, que a 2ª renomeou para Long Function e Repeated Switches). O agrupamento em famílias abaixo (Bloaters, Couplers etc.) é a classificação didática de Mäntylä, popularizada pelo Refactoring.Guru — não é a organização do capítulo de Fowler. Um smell é uma característica do código que, por experiência, costuma indicar um problema de design mais fundo. Ele não prova o problema — apenas sugere onde cheirar. A cura é sempre uma ou mais Técnicas de Refactoring, e a meta é reaproximar-se de Clean Code.
mindmap
root((Code Smells))
Bloaters
Long Method
Large Class
Long Parameter List
Primitive Obsession
OO Abusers
Switch Statements
Refused Bequest
Change Preventers
Divergent Change
Shotgun Surgery
Dispensables
Duplicated Code
Dead Code
Comments
Couplers
Feature Envy
Inappropriate Intimacy
Message Chains
Bloaters — código, métodos e classes que incharam
Seção intitulada “Bloaters — código, métodos e classes que incharam”Estruturas que cresceram além do gerenciável, geralmente por acúmulo gradual.
| Smell | Sintoma | Refactoring que costuma curar |
|---|---|---|
| Long Method | Método longo, muitos níveis de abstração, difícil de nomear inteiramente | Extract Function, Replace Temp with Query |
| Large Class | Classe com campos e responsabilidades demais | Extract Class — viola SRP |
| Long Parameter List | 4+ parâmetros, alguns sempre juntos | Introduce Parameter Object |
| Primitive Obsession | Uso de String/int para conceitos de domínio (CPF, dinheiro, e-mail) |
Replace Primitive with Object → Value Objects |
Primitive Obsession em Java
Seção intitulada “Primitive Obsession em Java”public void transferir(String contaOrigem, String contaDestino, long valorEmCentavos) { if (valorEmCentavos <= 0) throw new IllegalArgumentException();}public void transferir(NumeroConta origem, NumeroConta destino, Dinheiro valor) { valor.exigirPositivo();}O tipo Dinheiro (um Value Object) concentra validação e semântica que, com primitivos, ficariam espalhadas por todo lugar.
Object-Orientation Abusers — OO aplicado de forma incompleta
Seção intitulada “Object-Orientation Abusers — OO aplicado de forma incompleta”| Smell | Sintoma | Refactoring que costuma curar |
|---|---|---|
| Switch Statements | switch/if-else sobre um tipo que se repete pelo código |
Replace Conditional with Polymorphism → habilita OCP |
| Refused Bequest | Subclasse herda mas ignora ou rejeita métodos/campos da superclasse | Push Down, Replace Inheritance with Delegation — viola LSP |
public double calcularArea(Forma f) { switch (f.getTipo()) { case CIRCULO: return Math.PI * f.getRaio() * f.getRaio(); case QUADRADO: return f.getLado() * f.getLado(); default: throw new IllegalArgumentException(); }}Cada nova forma força editar este switch (e todos os outros iguais). A cura polimórfica está em Replace Conditional with Polymorphism.
Change Preventers — mudanças que doem
Seção intitulada “Change Preventers — mudanças que doem”Estruturas que fazem uma alteração simples exigir esforço desproporcional.
| Smell | Sintoma | Refactoring que costuma curar |
|---|---|---|
| Divergent Change | Uma classe muda por muitas razões diferentes | Extract Class / Split Phase — viola SRP |
| Shotgun Surgery | Uma mudança exige editar muitas classes espalhadas | Move Method/Field, Inline Class — viola OCP |
Divergent Change e Shotgun Surgery são espelhos: o primeiro é “uma classe, muitas razões de mudança”; o segundo é “uma razão de mudança, muitas classes”. Ambos indicam responsabilidades mal alocadas.
Dispensables — o que deveria simplesmente sumir
Seção intitulada “Dispensables — o que deveria simplesmente sumir”| Smell | Sintoma | Refactoring que costuma curar |
|---|---|---|
| Duplicated Code | Mesma estrutura em vários lugares | Extract Function, Pull Up Method — o smell nº 1 a caçar |
| Dead Code | Código nunca executado (variável, parâmetro, método, branch) | Remove Dead Code, Inline |
| Comments | Comentários que compensam código ruim | Extract Function, Rename — ver Clean Code |
Couplers — acoplamento excessivo entre objetos
Seção intitulada “Couplers — acoplamento excessivo entre objetos”| Smell | Sintoma | Refactoring que costuma curar |
|---|---|---|
| Feature Envy | Método interessado mais nos dados de outra classe que na própria | Move Method, Extract Function |
| Inappropriate Intimacy | Duas classes conhecem detalhes internos uma da outra | Move Method/Field, Hide Delegate |
| Message Chains | a.getB().getC().getD().fazer() |
Hide Delegate, Extract Function (Lei de Deméter) |
Feature Envy em Java
Seção intitulada “Feature Envy em Java”public class Fatura { public double desconto(Cliente cliente) { if (cliente.getPontos() > 1000 && cliente.getCategoria().equals("OURO")) { return 0.15; } return 0; }}O método desconto usa só dados de Cliente — inveja as features dele. A cura é mover a lógica para Cliente.percentualDeDesconto().
Ponto-chave: smells são violações de SOLID vistas do código
Seção intitulada “Ponto-chave: smells são violações de SOLID vistas do código”O mapeamento não é acidental — smells e princípios descrevem a mesma tensão de design por ângulos diferentes:
flowchart LR
S1[Shotgun Surgery] --> P1[OCP]
S2[Large Class / Divergent Change] --> P2[SRP]
S3[Refused Bequest] --> P3[LSP]
S4[Switch Statements] --> P1
O mapa completo está em Code Smells e SOLID. Reconhecer o smell te dá o gatilho; conhecer o princípio te diz a direção da cura.
Anti-padrões e erros comuns
Seção intitulada “Anti-padrões e erros comuns”- Tratar smell como bug — smell é indício, não defeito; às vezes o design atual é o certo para o contexto.
- Caçar smells sem testes — remover um smell sem rede de segurança arrisca comportamento (ver Fundamentos de Refactoring).
- Ignorar Duplicated Code — é o smell mais barato de detectar e o de maior retorno ao remover.
- Confundir Comments legítimos com o smell Comments — o smell é o comentário que existe para desculpar código ruim, não a documentação de intenção genuína.
- Purismo — remover todo smell cegamente pode superengenheirar; pese o custo da mudança.
Relações
Seção intitulada “Relações”- Sintoma da tríade: Clean Code e Refactoring
- Meta violada: Clean Code
- Cura: Técnicas de Refactoring
- Método seguro: Fundamentos de Refactoring
- Mapa smell ↔ princípio: Code Smells e SOLID · SOLID · SRP - Single Responsibility Principle · OCP - Open-Closed Principle · LSP - Liskov Substitution Principle
- Cura de Primitive Obsession: Value Objects
Perguntas de revisão
Seção intitulada “Perguntas de revisão”1. O que distingue um code smell de um bug?
Resposta
Um bug é comportamento incorreto; um smell é uma característica estrutural que, por experiência, indica um problema de design mais profundo. O smell não prova nada — só sugere onde investigar. Às vezes o design “com cheiro” é o adequado para o contexto.
2. Qual a diferença entre Divergent Change e Shotgun Surgery?
Resposta
São espelhos. Divergent Change: uma classe muda por muitas razões diferentes (viola SRP). Shotgun Surgery: uma única razão de mudança obriga editar muitas classes espalhadas (viola OCP). Ambos apontam responsabilidades mal distribuídas.
3. Como Primitive Obsession se relaciona com Value Objects?
Resposta
Primitive Obsession é usar tipos primitivos (String, int) para representar conceitos de domínio como CPF, dinheiro ou e-mail. A cura é Replace Primitive with Object, introduzindo um Value Object que concentra validação e semântica antes espalhadas. Ver Value Objects.
4. Por que se diz que muitos smells são violações de SOLID?
Resposta
Porque smells e princípios descrevem a mesma tensão de design por ângulos diferentes: Large Class/Divergent Change ↔ SRP, Shotgun Surgery e Switch Statements ↔ OCP, Refused Bequest ↔ LSP. O smell é o gatilho observável; o princípio indica a direção da cura. Ver Code Smells e SOLID.