Pular para o conteúdo
GoF Padrões de Projeto Criacionais
Criacional escopo de objeto
GoF, p. 97

Construtor

Builder

Monta objetos complexos passo a passo — o mesmo processo gera representações diferentes.

Complexidade média Frequência de uso muito comum
Intençãocomo o livro define

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.

Analogiauma imagem do mundo real

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.

O problema

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.

A solução

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.

Sintomascomo reconhecer no seu código
  • new Pedido(null, null, true, 0, null, "", false)
  • Subclasses nomeadas por combinação de features.

Estrutura

dirigeproduzproduzDirector+ construir(b)«interface»Builder+ reset()+ paredes()+ telhado()BuilderCasa+ getResultado()BuilderPlanta+ getResultado()CasaPlantaSVG
Fig. 2 — Builder herda / implementa cria usa / contém
Ver este diagrama sendo desenhado, traço a traço
Participantes
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

Listagem 2 Montando uma consulta SQL passo a passo
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());
Na práticaonde ele já existe
  • java.lang.StringBuilder / StringBuffer
  • java.util.stream.Stream.builder()
  • Query builders de ORMs (Knex, QueryBuilder do Doctrine, ActiveRecord)

Consequências

A favor
  • 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.
Contra
  • Aumenta o número de classes.
  • Se o objeto é simples, é cerimônia pura.
Use quando
  • 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.
Evite quando
  • O objeto tem 2 ou 3 campos — um construtor simples ou um objeto de opções resolve.

Relações com outros padrões

parecido / fácil de confundir
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.
costuma andar junto
Composite
Builder é excelente para montar árvores Composite recursivamente.
Singleton
O Director costuma ser único na aplicação.

Verificação

Questãoa resposta está marcada

O Director em um Builder é responsável por:

  1. Saber a ORDEM e a combinação das etapas — a “receita” reutilizável
  2. Criar o objeto final e devolvê-lo ao cliente
  3. Escolher qual classe concreta de produto instanciar
  4. 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.