---
id: design-patterns-gof
domain: architecture
agents: [architect]
when: "ao reconhecer um problema recorrente de design e escolher (ou descartar) um padrão"
---

# Design Patterns (GoF) — índice sintoma → padrão → trade-off

23 soluções nomeadas para problemas recorrentes de design OO, catalogadas pelo Gang of Four (1994)
e mantidas com critérios atuais em [refactoring.guru](https://refactoring.guru/design-patterns).
Este pack é o **índice de decisão**: para cada padrão, o problema que resolve, o code smell que
sinaliza que ele cabe, um exemplo minimal em TypeScript, e — o que mais importa — **quando NÃO usar**,
porque um padrão aplicado fora de hora é a forma mais cara de over-engineering.

## O problema

Reconhecer um padrão é fácil; saber se ele **cabe** é o craft. Dois erros simétricos:

1. **Reinventar.** Resolver na unha um problema que já tem solução nomeada, testada e comunicável —
   e errar nos casos de borda que o padrão já cobre.
2. **Cargo-culting.** Aplicar o padrão porque você sabe o nome, não porque o problema pediu. Uma
   `AbstractFactoryBuilderStrategy` para instanciar uma struct de config. Isto é o tell de IA da
   arquitetura: estrutura sem justificativa. A régua do pack-âncora vale aqui — **uma decisão de
   design só existe se você consegue justificar por que ela é assim e não o default.**

A fonte é explícita sobre o custo: quase todo padrão lista, nas próprias contras, *"o código fica
mais complicado"* / *"novas interfaces e classes são introduzidas"*. Padrão é dívida de indireção
paga adiantada na esperança de flexibilidade futura. Só vale se a flexibilidade **vier**.

> Os padrões variam em escala: **idiomas** são o nível mais baixo (presos a uma linguagem);
> **padrões arquiteturais** são o mais alto (desenham a aplicação inteira). Os 23 GoF ficam no meio —
> design de classes e objetos, não arquitetura de sistema. Não confunda escolher um GoF com decidir
> arquitetura.

## Os princípios / o conhecimento

### As 3 categorias (intenção — definição da fonte)

| Categoria | O que resolve (definição refactoring.guru) | Eixo |
|---|---|---|
| **Creational** (5) | "mecanismos de criação de objetos que aumentam flexibilidade e reuso do código existente" | *como nascem* os objetos |
| **Structural** (7) | "como montar objetos e classes em estruturas maiores, mantendo-as flexíveis e eficientes" | *como se compõem* |
| **Behavioral** (11) | "comunicação efetiva e atribuição de responsabilidades entre objetos" | *como conversam* |

### Creational — sintoma → padrão → quando NÃO

| Padrão | Code smell que sinaliza | Resolve | NÃO use quando |
|---|---|---|---|
| **Factory Method** | `new ConcreteX()` espalhado; `switch(type)` para instanciar | criar objetos cujo tipo exato você não sabe de antemão; deixar o framework ser estendido por subclasse | só há 1 tipo concreto e não há hierarquia de criadores — vira subclasse à toa |
| **Abstract Factory** | criar **famílias** de objetos que precisam ser compatíveis entre si (botão+checkbox do mesmo tema) | produzir famílias relacionadas sem amarrar a classes concretas | os produtos não formam família / não precisam casar — interfaces demais sem ganho |
| **Builder** | **construtor telescópico**: `new X(a,b,c,d,null,null,true)` | construir objeto complexo passo a passo; mesmas etapas, representações diferentes | objeto simples com poucos campos — o builder é mais código que o construtor |
| **Prototype** | subclasses que só diferem em **como inicializam**; código de init repetido | clonar objetos sem depender da classe concreta | objetos com referências circulares (a própria fonte avisa: clonar fica "muito complicado") |
| **Singleton** | querer "uma instância só" e ponto de acesso global | garantir instância única + acesso global | **quase sempre.** Viola SRP, mascara acoplamento, complica teste (construtor privado, sem mock por herança), exige cuidado com threads. Prefira injeção de dependência |

```ts
// Factory Method — esconde o `new` concreto atrás de uma escolha
interface Transport { deliver(): string; }
class Truck implements Transport { deliver() { return "via estrada"; } }
class Ship implements Transport { deliver() { return "via mar"; } }

abstract class Logistics {
  abstract createTransport(): Transport;       // o "factory method"
  planDelivery() { return this.createTransport().deliver(); }
}
class RoadLogistics extends Logistics { createTransport() { return new Truck(); } }
class SeaLogistics  extends Logistics { createTransport() { return new Ship(); } }
```

### Structural — sintoma → padrão → quando NÃO

| Padrão | Code smell que sinaliza | Resolve | NÃO use quando |
|---|---|---|---|
| **Adapter** | interface incompatível: classe legada/3rd-party não encaixa no seu código | fazer interfaces incompatíveis colaborarem (tradutor) | você pode simplesmente alterar a classe de serviço — a fonte diz: às vezes editar direto é mais simples |
| **Bridge** | explosão combinatória de subclasses em 2 eixos (Forma × Cor) | separar abstração de implementação em hierarquias independentes; trocar impl em runtime | a classe é altamente coesa — a fonte avisa que aplicar aqui só complica |
| **Composite** | `if (isLeaf) … else (itera filhos) …`; tratar nó e folha diferente | árvore de objetos tratada uniformemente via interface comum (polimorfismo + recursão) | as classes diferem demais para uma interface comum — você supergeneraliza |
| **Decorator** | subclasses combinatórias para empilhar comportamentos (`BufferedEncryptedStream`) | adicionar comportamento em runtime empilhando wrappers | comportamento depende da **ordem** do empilhamento, ou você precisa remover um wrapper específico (a fonte cita ambos como difíceis) |
| **Facade** | cliente precisa orquestrar 10 classes do subsistema na ordem certa | uma interface simples para um subsistema complexo | a facade vira **god object** acoplado a tudo — sinal de que virou problema, não solução |
| **Flyweight** | **milhões** de objetos similares estourando a RAM, com estado duplicável | compartilhar estado intrínseco entre muitos objetos para caber na RAM | NÃO há problema real de RAM. A fonte é taxativa: *"use only when"* há explosão de objetos drenando memória. Caso contrário troca RAM por CPU e ofusca o código |
| **Proxy** | precisa de lazy-init / controle de acesso / cache / log **transparente** ao redor de um objeto | substituto que controla acesso (virtual, protection, remote, logging, caching, smart-ref) | uma chamada direta resolve — proxy adiciona classe e latência sem motivo |

```ts
// Decorator — empilha comportamento sem subclasse combinatória
interface DataSource { write(s: string): string; }
class FileSource implements DataSource { write(s: string) { return s; } }

abstract class Decorator implements DataSource {
  constructor(protected wrappee: DataSource) {}
  write(s: string) { return this.wrappee.write(s); }
}
class Encrypt extends Decorator { write(s: string) { return super.write(`enc(${s})`); } }
class Compress extends Decorator { write(s: string) { return super.write(`zip(${s})`); } }

new Encrypt(new Compress(new FileSource())).write("dado"); // enc(zip(dado)) — ordem importa!
```

### Behavioral — sintoma → padrão → quando NÃO

| Padrão | Code smell que sinaliza | Resolve | NÃO use quando |
|---|---|---|---|
| **Chain of Responsibility** | série de `if/else` decidindo quem trata o request, em ordem | passar request por cadeia de handlers; cada um trata ou repassa | a fonte avisa: um request pode **acabar sem tratamento** — ruim se todo request precisa de resposta |
| **Command** | querer enfileirar / agendar / desfazer / logar operações | encapsular request como objeto (undo/redo, fila, log, remoto) | a operação é direta e nunca precisa ser adiada/revertida — só adiciona camada sender↔receiver |
| **Iterator** | código de travessia duplicado; expor estrutura interna da coleção | percorrer coleção sem expor sua representação | coleção simples (array) — a fonte diz: overhead desnecessário e menos eficiente que acesso direto |
| **Mediator** | teia de dependências diretas N×N entre componentes (UI clássico) | centralizar a comunicação em um mediador | poucos componentes pouco acoplados — o mediador vira **god object** com o tempo |
| **Memento** | precisar de snapshot/restore sem violar encapsulamento (undo) | salvar/restaurar estado sem expor campos internos | snapshots frequentes (estoura RAM) ou linguagem dinâmica que não garante imutabilidade do memento (JS/TS incluídos — a fonte cita) |
| **Observer** | objetos que precisam reagir a mudanças de outro, lista dinâmica | pub/sub: notificar muitos assinantes sobre eventos | ordem de notificação importa — a fonte avisa que assinantes são notificados em ordem imprevisível |
| **State** | conditionals gigantes sobre `this.status` espalhados pela classe | objeto muda comportamento conforme estado interno (máquina de estados) | poucos estados que raramente mudam — a fonte diz que é exagero |
| **Strategy** | `switch` entre variantes do **mesmo** algoritmo; classes que só diferem num comportamento | família de algoritmos intercambiáveis em runtime | só há 1-2 algoritmos estáveis; ou a linguagem tem funções de 1ª classe — a fonte sugere passar uma função/lambda em vez de criar classes |
| **Template Method** | classes com algoritmos quase idênticos, diferindo em poucos passos | esqueleto do algoritmo na superclasse, passos sobrescritos por subclasse | cliente fica preso ao esqueleto; cresce em passos → difícil manter; risco de violar LSP ao suprimir passo |
| **Visitor** | adicionar operações a uma hierarquia estável sem tocar nas classes | separar algoritmo dos objetos sobre os quais opera (acumula info na travessia) | a hierarquia de elementos muda muito — a fonte avisa: cada novo elemento força atualizar **todos** os visitors |
| **Interpreter** *(GoF clássico, fora do catálogo refactoring.guru)** | gramática simples e recorrente a avaliar (filtros, expressões) | representar uma gramática como classes e interpretá-la | gramática não-trivial → use parser/AST de verdade. Raro o suficiente para a fonte nem catalogar |

```ts
// Strategy — algoritmos intercambiáveis. Em TS, muitas vezes uma função basta:
type PricingStrategy = (base: number) => number;
const retail: PricingStrategy  = b => b;
const black:  PricingStrategy  = b => b * 0.7;

class Cart {
  constructor(private price: PricingStrategy) {}        // injete a estratégia
  total(base: number) { return this.price(base); }
}
new Cart(black).total(100); // 70 — sem classes Strategy/ConcreteStrategy: a fonte aceita
```

### Pares que se confundem (escolha por intenção, não por estrutura)

| Você quer… | Use | NÃO confunda com |
|---|---|---|
| compatibilizar interface existente | **Adapter** | Facade (simplifica subsistema inteiro, não 1 classe) |
| adicionar responsabilidade transparente | **Decorator** | Proxy (controla acesso, mesma interface mas outro intento) / Adapter (muda interface) |
| trocar algoritmo de fora | **Strategy** | State (muda sozinho conforme estado interno; estados se conhecem) |
| esconder criação por subclasse | **Factory Method** | Abstract Factory (famílias) / Builder (passo a passo) |
| eixos independentes que variam | **Bridge** | Strategy (Bridge é decisão estrutural maior; Strategy é tática) |

### Padrões e refactoring — a relação

A fonte (refactoring.guru) trata os dois como complementares: **refactoring** é *"o processo
sistemático de melhorar código sem criar funcionalidade nova"*, e **code smells** são *"indicadores
de problemas… fáceis de notar, mas podem ser sintoma de um problema mais profundo."* O fluxo natural:

```
code smell  →  refactoring  →  (às vezes)  design pattern como destino do refactoring
```

Os smells **são** os gatilhos da coluna "sintoma" das tabelas acima — `switch(type)` pede Strategy/
State/Factory; construtor telescópico pede Builder; conditionals sobre status pedem State. O padrão
é o **alvo** de um refactoring guiado por smell — não um ponto de partida. Comece pelo smell, não
pelo padrão. Se não há smell, não há padrão a aplicar.

## Checklist — antes de introduzir um padrão

Qualquer "não" é um sinal de over-engineering — pare:

- [ ] Existe um **code smell concreto e nomeável** hoje (não "pode ser que no futuro")?
- [ ] Consigo justificar por que a indireção extra **se paga** com flexibilidade que vou usar?
- [ ] Escolhi pela **intenção** do padrão, não pela estrutura parecida (Strategy vs State, Adapter vs Facade)?
- [ ] Considerei a alternativa mais simples da própria fonte (função/lambda em vez de Strategy; editar a classe em vez de Adapter; DI em vez de Singleton)?
- [ ] Li a seção **"NÃO use quando"** deste padrão e nenhum item bate no meu caso?
- [ ] Se é Singleton/Facade/Mediator: tenho como evitar o god-object / o acoplamento global?
- [ ] Estou no nível certo (design de classe), e não confundindo isto com decisão **arquitetural**?

## Tabela de decisão "use X quando Y"

| Quando Y (o sintoma)… | Use X | Categoria |
|---|---|---|
| não sei o tipo concreto a instanciar | Factory Method | Creational |
| preciso de famílias de objetos compatíveis | Abstract Factory | Creational |
| construtor com muitos parâmetros opcionais | Builder | Creational |
| subclasses só diferem na inicialização | Prototype | Creational |
| (raramente) instância única global controlada | Singleton — prefira DI | Creational |
| interface incompatível com a minha | Adapter | Structural |
| explosão de subclasses em 2+ eixos | Bridge | Structural |
| estrutura em árvore tratada uniformemente | Composite | Structural |
| empilhar comportamentos em runtime | Decorator | Structural |
| esconder a complexidade de um subsistema | Facade | Structural |
| milhões de objetos similares estourando RAM | Flyweight | Structural |
| controlar acesso (lazy/cache/log/remoto) transparente | Proxy | Structural |
| múltiplos handlers possíveis, em ordem | Chain of Responsibility | Behavioral |
| enfileirar / agendar / desfazer operações | Command | Behavioral |
| percorrer coleção sem expor a estrutura | Iterator | Behavioral |
| teia N×N de dependências entre componentes | Mediator | Behavioral |
| snapshot/restore sem violar encapsulamento | Memento | Behavioral |
| muitos objetos reagindo a um evento | Observer | Behavioral |
| comportamento muda conforme estado interno | State | Behavioral |
| trocar variante de algoritmo em runtime | Strategy | Behavioral |
| algoritmos quase iguais com poucos passos variáveis | Template Method | Behavioral |
| operações novas sobre hierarquia estável | Visitor | Behavioral |
| avaliar uma gramática simples recorrente | Interpreter | Behavioral |

> Fonte dos critérios, intents e prós/contras: [refactoring.guru/design-patterns](https://refactoring.guru/design-patterns).
> O catálogo da fonte cobre 22 padrões; o GoF clássico inclui **Interpreter** (23º), aqui mantido por completude.
