# Code smells e refactoring

Cheiros típicos de código doente e as refatorações que os resolvem sem mudar comportamento.

Página: https://resumos.rgo.pt/cadeiras/ldts/smells-refactoring/

Um **code smell** é um sintoma de mau desenho: o código funciona, mas cheira a problema futuro. **Refactoring** é a cura: reestruturar o código sem mudar o comportamento observável. A rede de segurança são os testes da página anterior. A regra de ouro: nunca refatores sem testes verdes, e corre-os a cada passo pequeno. O catálogo clássico destes cheiros e curas está documentado no refactoring.guru.[1](https://resumos.rgo.pt/cadeiras/ldts/smells-refactoring/#user-content-fn-catalogo)

## Os cheiros mais comuns

**Método longo**: um método com cinquenta linhas faz várias coisas. Resolve-se com **extrair método**, dando um nome a cada bloco.

**Classe grande**: a `Jogo` que sabe de input, física, desenho e pontuação. Resolve-se com **extrair classe**, como fizeste no SOLID.

**Código duplicado**: o mesmo bloco em três sítios. Cada correção futura tem de ser feita três vezes, e uma vai ser esquecida. Resolve-se com **extrair método** e chamar o método nos três sítios.

**Feature Envy**: um método que usa mais os dados de outra classe que os seus. O método tem inveja da outra classe e devia mudar-se para lá com **mover método**.

**Data class**: uma classe só com getters e setters, sem comportamento. O comportamento vive espalhado por quem usa a classe. Resolve-se a mover os métodos para dentro dela.

**Switch extenso**: um `switch` sobre o tipo que cresce a cada funcionalidade nova. Resolve-se com polimorfismo: cada caso vira uma classe, como os `Comando` do SOLID.

**Data Clumps**: os mesmos três parâmetros (`x`, `y`, `cor`) a viajarem juntos em cinco assinaturas. Resolve-se com **extrair classe**, agrupando-os num objeto como `Posicao`.

**Long Parameter List**: um método com seis ou sete parâmetros que ninguém consegue chamar sem consultar a assinatura. Resolve-se a agrupar parâmetros relacionados num objeto ou a dividir o método.

**Primitive Obsession**: usar `String` para telefones, `int` para dinheiro, `double` para posições. Resolve-se com **substituir primitiva por objeto**, criando pequenos tipos com validação própria.

Método longo esconde várias intenções num bloco só. A cura é **extrair método**: cada bloco ganha um nome que diz o que faz, e o método original passa a ler-se como uma lista de passos. No exemplo abaixo, `atualizar` passou de onze linhas para duas chamadas com nome.

Feature Envy é um método que vive nos dados de outra classe. A cura é **mover método**: o cálculo muda-se para a classe cujos dados consulta, e o método original delega. O `moverMonstro` foi morar na `Arena` porque é lá que vivem limites, ocupação, vento e gravidade.

Switch extenso sobre o tipo cresce a cada caso novo. A cura é **substituir condicional por polimorfismo**: cada caso vira uma classe com o mesmo método, e a escolha passa a ser que objeto construir. Os `Comando` do SOLID são este padrão aplicado às teclas.

## Como refatorar em segurança

Refactoring faz-se em passos minúsculos, cada um com os testes verdes no fim:

1.  Garante que há um teste a cobrir o código que vais mexer.
2.  Faz uma mudança pequena: extrai um método, muda um nome, move uma linha.
3.  Corre os testes. Se falharem, desfaz e tenta de outra forma.
4.  Só quando está verde é que dás o passo seguinte.

Refactoring não é reescrever

Se mudas comportamento e estrutura ao mesmo tempo, e algo parte, não sabes qual das duas mudanças causou. Primeiro refatora com testes verdes, depois muda comportamento, depois refatora outra vez.

## Exemplo: método longo com Feature Envy

Este método atualiza um monstro e cheira mal duas vezes: é longo e passa a vida nos dados da arena:

```
void atualizar(Monstro m, Arena arena) {
    Posicao p = m.getPosicao();
    int nx = p.getX() + arena.getVentoX();
    int ny = p.getY() + arena.getGravidade();
    if (arena.dentroLimites(nx, ny) && !arena.ocupado(nx, ny)) {
        m.setPosicao(new Posicao(nx, ny));
    }
    arena.contarPasso();
    if (arena.getPassos() % 10 == 0) {
        arena.surgirItem();
    }
}
```

O método usa `arena` cinco vezes e `m` duas: tem inveja da arena. A versão refatorada move o cálculo para onde os dados vivem e extrai métodos com nome. Corre e confirma que o monstro anda uma casa e o relógio avança:

```java
import java.util.ArrayList;
import java.util.List;

class Posicao {
    private final int x;
    private final int y;

    Posicao(int x, int y) {
        this.x = x;
        this.y = y;
    }

    int getX() { return x; }
    int getY() { return y; }
}

class Monstro {
    private Posicao posicao;

    Monstro(Posicao posicao) {
        this.posicao = posicao;
    }

    Posicao getPosicao() { return posicao; }
    void setPosicao(Posicao posicao) { this.posicao = posicao; }
}

class Arena {
    private final List<Monstro> monstros = new ArrayList<>();
    private int passos = 0;

    void adicionar(Monstro monstro) {
        monstros.add(monstro);
    }

    void moverMonstro(Monstro monstro) {
        Posicao atual = monstro.getPosicao();
        monstro.setPosicao(new Posicao(atual.getX() + 1, atual.getY()));
    }

    void avancarRelogio() {
        passos++;
    }

    void atualizar(Monstro monstro) {
        moverMonstro(monstro);
        avancarRelogio();
    }

    int getPassos() { return passos; }
}

public class Main {
    public static void main(String[] args) {
        Arena arena = new Arena();
        Monstro monstro = new Monstro(new Posicao(2, 3));
        arena.adicionar(monstro);
        arena.atualizar(monstro);
        arena.atualizar(monstro);
        Posicao fim = monstro.getPosicao();
        System.out.println("posicao: (" + fim.getX() + "," + fim.getY() + ")");
        System.out.println("passos: " + arena.getPassos());
    }
}
```

A saída mostra `posicao: (4,3)` e `passos: 2`. O `moverMonstro` fica na `Arena`, junto dos dados que consulta. O `avancarRelogio` esconde a contagem. O método original passou de onze linhas para duas chamadas com nome, e os testes continuam verdes porque nada observável mudou.

[Vídeo: Get a Whiff of This, Sandi Metz na RailsConf 2016](https://www.youtube.com/watch?v=PJjHfa5yxlU)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=PJjHfa5yxlU)

## Notas de rodapé

1.  Refactoring.guru, catálogo de code smells e refatorações com exemplos ([https://refactoring.guru/](https://refactoring.guru/)). [Voltar](https://resumos.rgo.pt/cadeiras/ldts/smells-refactoring/#user-content-fnref-catalogo)
