GoF, p. 97
Construtor
Builder
Monta objetos complexos passo a passo — o mesmo processo gera representações diferentes.
Separar a construção de um objeto complexo da sua representação, de modo que o mesmo processo de construção possa criar representações diferentes.
Construir uma casa
Toda casa precisa de paredes, piso, porta e telhado. Mas uma pode ter piscina, outra aquecimento central, outra um jardim de inverno. Você não cria um construtor com 20 parâmetros (a maioria vazios) nem uma subclasse `CasaComPiscinaSemGaragem`. Você chama um mestre de obras e pede as etapas que quer. Se pedir a um mestre de obras diferente (o arquiteto que desenha plantas), o mesmo roteiro de etapas produz uma planta em vez de uma casa.
Um objeto exige uma inicialização longa e cheia de variações opcionais. As duas saídas ingênuas são péssimas: um construtor telescópico com dezenas de parâmetros (`new Casa(4, 2, true, null, false, …)` — ilegível) ou uma explosão de subclasses para cada combinação possível.
Extraia as etapas de construção para uma interface Builder. Cada builder concreto implementa as etapas de um jeito e sabe devolver seu próprio resultado. Um Director (opcional) guarda receitas de construção reutilizáveis — a ordem das chamadas.
new Pedido(null, null, true, 0, null, "", false)- Subclasses nomeadas por combinação de features.
Estrutura
- Builder
- Interface com as etapas de construção comuns a todos os produtos.
- ConcreteBuilder
- Implementa as etapas e mantém o produto em construção; expõe `getResultado()`.
- Director
- Conhece a ORDEM das etapas. Encapsula receitas reutilizáveis. É opcional.
- Product
- O objeto resultante. Não precisa ter interface comum entre builders.
Implementação
class ConsultaBuilder {
constructor() { this.reset(); }
reset() { this._partes = { select: '*', from: '', where: [], order: null, limit: null }; return this; }
// Cada etapa devolve `this` → interface fluente (encadeável).
selecionar(...campos) { this._partes.select = campos.join(', '); return this; }
de(tabela) { this._partes.from = tabela; return this; }
onde(cond) { this._partes.where.push(cond); return this; }
ordenarPor(campo) { this._partes.order = campo; return this; }
limitar(n) { this._partes.limit = n; return this; }
// O produto só é entregue no final — e o builder se reinicia.
build() {
const p = this._partes;
if (!p.from) throw new Error('Consulta sem tabela de origem');
let sql = `SELECT ${p.select} FROM ${p.from}`;
if (p.where.length) sql += ` WHERE ${p.where.join(' AND ')}`;
if (p.order) sql += ` ORDER BY ${p.order}`;
if (p.limit != null)sql += ` LIMIT ${p.limit}`;
this.reset();
return sql;
}
}
// ── Director: guarda RECEITAS reutilizáveis ───────────
class RelatorioDirector {
usuariosAtivos(builder) {
return builder
.selecionar('id', 'nome', 'email')
.de('usuarios')
.onde("status = 'ativo'")
.ordenarPor('nome')
.limitar(100)
.build();
}
}
const b = new ConsultaBuilder();
console.log(new RelatorioDirector().usuariosAtivos(b));
// SELECT id, nome, email FROM usuarios WHERE status = 'ativo' ORDER BY nome LIMIT 100
// O mesmo builder serve para consultas ad-hoc, sem Director:
console.log(b.de('pedidos').onde('total > 1000').build()); - java.lang.StringBuilder / StringBuffer
- java.util.stream.Stream.builder()
- Query builders de ORMs (Knex, QueryBuilder do Doctrine, ActiveRecord)
Consequências
- Constrói objetos passo a passo, adiando ou repetindo etapas.
- Reutiliza o mesmo código de construção para produtos bem diferentes.
- Isola a lógica de montagem complexa da regra de negócio.
- Permite validar o objeto antes de entregá-lo — nunca existe um objeto “meio pronto” visível.
- Aumenta o número de classes.
- Se o objeto é simples, é cerimônia pura.
- Você se pegou escrevendo um construtor com muitos parâmetros opcionais.
- O mesmo processo de montagem deve gerar representações diferentes (HTML, JSON, PDF…).
- Você quer construir árvores (Composite) etapa por etapa.
- Você quer objetos imutáveis com muitos campos configuráveis.
- O objeto tem 2 ou 3 campos — um construtor simples ou um objeto de opções resolve.
Relações com outros padrões
- Abstract Factory
- Abstract Factory entrega o produto de imediato; Builder deixa você intervir entre as etapas.
- Bridge
- Director é a abstração; builders são as implementações.
Verificação
O Director em um Builder é responsável por:
- Saber a ORDEM e a combinação das etapas — a “receita” reutilizável
- Criar o objeto final e devolvê-lo ao cliente
- Escolher qual classe concreta de produto instanciar
- Garantir que só exista uma instância do builder
O Director conhece a sequência de etapas. Quem guarda e devolve o produto é o ConcreteBuilder — por isso `getResultado()` não fica na interface Builder: produtos diferentes podem não ter nada em comum.