A linha de montagem da fábrica
A fábrica define a esteira e as etapas de montagem uma única vez. Cada unidade especializada decide qual modelo sai da linha — sedã, hatch ou picape — sem que o processo geral precise mudar. O cliente pede 'um carro' e recebe o produto certo; quem escolhe o modelo concreto é a subclasse, não quem faz o pedido.
O problema
O código cliente precisa criar objetos, mas não deveria depender das classes concretas. Cada novo tipo obrigaria a alterar o cliente, violando o princípio Aberto/Fechado.
Um new ClasseConcreta() espalhado pelo código acopla o cliente a implementações específicas e dificulta tanto os testes quanto futuras extensões.
A raiz está numa assimetria: você programa contra interfaces em todo lugar — parâmetros, retornos, campos — menos na hora de instanciar. O new é o único ponto do polimorfismo que não é polimórfico: ele exige o nome da classe concreta ali, em tempo de compilação. Todo lugar que escreve new fica amarrado àquela implementação, e a criação é justamente o detalhe que mais tende a variar (por plataforma, ambiente, formato, plano do cliente).
O padrão resolve movendo o new para um método que subclasses sobrescrevem — e é aí que ele se diferencia das alternativas. Uma função factory simples (criarBotao(tipo)) centraliza a escolha num switch e basta para casos diretos; um container de injeção de dependência resolve quando a decisão é de configuração. Factory Method vale quando a criação depende de um contexto que a subclasse conhece e o método criador participa de um algoritmo maior da classe base — o clássico par com Template Method.
Também não confunda com Abstract Factory: o Factory Method cria um produto por vez, via herança; o Abstract Factory agrupa a criação de uma família inteira de produtos relacionados (botão + input + modal do mesmo tema), via composição. Na prática, Abstract Factories costumam ser implementadas como um conjunto de factory methods.
- Cliente
- chama operacao()Criador
- delega a criaçãofactoryMethod()
- instanciaProduto concreto
- 1
Declarar o factory method
A classe/interface base declara um método de criação que retorna a abstração do produto.
- 2
Sobrescrever na subclasse
Cada subclasse implementa o método para devolver o produto concreto adequado.
- 3
Chamar via abstração
O cliente usa operacao(), que internamente chama o factory method — sem saber qual concreto vem.
- 4
Estender sem alterar
Um novo produto exige apenas uma nova subclasse; o cliente permanece intocado.
Navegue pelas camadas
Camada 1/4Cliente
use ← →Camada 1
Cliente
usa o criador sem conhecer concretos
Conhece apenas a classe base do criador e a interface do produto. Chama operacao() e recebe o resultado — qual produto concreto foi instanciado é invisível para ele. É essa ignorância que permite trocar a plataforma, o banco ou o formato sem tocá-lo.
No código
function render(dialogo: Dialogo) {
return dialogo.renderizar(); // não sabe qual botão sai
}Se violar: um new BotaoWeb() no cliente esvazia o padrão: a dependência da classe concreta que você queria remover voltou, e com ela a mudança em cascata a cada tipo novo.
Estrutura
Código
interface Botao {
render(): string;
}
class BotaoWeb implements Botao {
render() { return "<button>"; }
}
class BotaoMobile implements Botao {
render() { return "TouchableOpacity"; }
}
abstract class Dialogo {
abstract criarBotao(): Botao; // factory method
renderizar() { return this.criarBotao().render(); }
}
class DialogoWeb extends Dialogo {
criarBotao() { return new BotaoWeb(); }
}
new DialogoWeb().renderizar(); // "<button>"from abc import ABC, abstractmethod
class Botao(ABC):
@abstractmethod
def render(self) -> str: ...
class BotaoWeb(Botao):
def render(self): return "<button>"
class Dialogo(ABC):
@abstractmethod
def criar_botao(self) -> Botao: ... # factory method
def renderizar(self): return self.criar_botao().render()
class DialogoWeb(Dialogo):
def criar_botao(self): return BotaoWeb()
DialogoWeb().renderizar() # "<button>"Casos de uso
SDK multi-plataforma
Cenário
Uma biblioteca de UI roda em web e mobile: o fluxo do diálogo (abrir, validar, confirmar) é idêntico, mas o botão é <button> num lado e TouchableOpacity no outro.
Como o padrão entra
Dialogo define renderizar() uma vez; DialogoWeb e DialogoMobile sobrescrevem apenas criarBotao(). Quem usa o SDK pode até criar DialogoDesktop próprio — o ponto de extensão é público.
Trade-off: Cada plataforma nova adiciona um par criador+produto; a hierarquia cresce em dupla e precisa de disciplina para não divergir.
Conexões de banco por ambiente
Cenário
A aplicação usa Postgres em produção, mas os testes precisam rodar em segundos com SQLite em memória, sem subir container.
Como o padrão entra
O repositório base declara criarConexao(): Conexao e escreve as queries contra a abstração; o ambiente decide qual subclasse entra. A suíte de testes ganha velocidade sem duplicar lógica de acesso.
Trade-off: Diferenças reais de SQL e de comportamento entre os bancos podem vazar pela abstração — o contrato só segura o que os dois suportam, e o teste em SQLite não prova tudo.
Parsers por formato de arquivo
Cenário
Um importador aceita CSV, XML e JSON de parceiros diferentes e precisa devolver os mesmos registros normalizados, com formatos novos chegando por contrato.
Como o padrão entra
Importador define o pipeline (ler, validar, gravar) e delega a criarParser(); cada formato vira uma subclasse. Formato novo = subclasse nova, pipeline intocado.
Trade-off: Formatos muito diferentes (streaming linha a linha vs documento inteiro em memória) forçam a interface do parser a nivelar por baixo ou a crescer demais.
A fábrica que conhece todos os produtos
Compila, roda, passa no code review. E quebra depois.
Em vez de cada subclasse decidir o que criar, existe uma função estática com um switch que conhece todas as implementações. É o mesmo new espalhado de antes, agora concentrado num único ponto de acoplamento.
class FabricaDeRelatorio {
static criar(tipo: string): Relatorio {
switch (tipo) {
case "pdf": return new RelatorioPdf();
case "csv": return new RelatorioCsv();
case "xlsx": return new RelatorioXlsx();
case "html": return new RelatorioHtml();
// ... e mais oito
default: throw new Error("tipo desconhecido: " + tipo);
}
}
}
// Este arquivo importa TODAS as implementacoes.
// Consequencias:
// - formato novo = editar aqui (Aberto/Fechado violado)
// - o bundle carrega os doze, mesmo usando um
// - o erro de tipo desconhecido so aparece em runtimeO que quebra
- Ao adicionar formatoUm arquivo central precisa ser editado sempre, e ele vira ponto de conflito de merge entre times.
- No bundleTodas as implementações são importadas junto, inclusive as que aquele fluxo nunca vai usar.
- Com tipo inválidoA falha acontece em tempo de execução, com uma string, em vez de em tempo de compilação.
A saída: Factory Method é polimorfismo: a subclasse que já sabe o contexto sobrescreve o método de criação. Quando o que se quer é mesmo mapear uma string vinda de fora, isso é um registro (Map<string, () => Produto>) preenchido por quem compõe a aplicação — e aí formato novo se registra, não edita.
Do cheiro ao padrão
Um passo por vez, com o motivo de cada um.
O cheiro: O new do exportador espalhado por cinco lugares, cada um decidindo o tipo por conta própria.
Como está. O new do exportador espalhado por cinco lugares, cada um decidindo o tipo por conta própria.
// relatorio-controller.ts
if (formato === "pdf") exportador = new ExportadorPdf(config);
else if (formato === "csv") exportador = new ExportadorCsv(config);
// agendador.ts — a mesma decisao, de novo
if (formato === "pdf") exportador = new ExportadorPdf(config);
else if (formato === "csv") exportador = new ExportadorCsv(config);
// E em outros tres arquivos. Formato novo = cacar todos.Armadilhas
Factory para classe única (YAGNI)
Se existe um único produto e nenhuma variação prevista, o factory method é cerimônia: classe abstrata, subclasse e override para embrulhar um new que uma função simples resolveria. Introduza o padrão quando a segunda variação aparecer de verdade — extrair a criação depois é uma refatoração barata.
Factory que retorna o tipo concreto
Declarar o método como criarBotao(): BotaoWeb em vez de retornar a interface mantém todo mundo acoplado à implementação — ganhou-se uma chamada a mais sem ganhar desacoplamento. O retorno do factory method é a abstração; se o chamador precisa saber o tipo exato, o padrão não está cumprindo o papel.
Hierarquias paralelas explodindo
Cada produto novo pede um criador novo: com dez formatos, são dez parsers e dez importadores. Se a subclasse do criador existe só para escolher o produto — sem lógica própria —, um registro (mapa de chave para construtor) ou Abstract Factory resolve com muito menos classe. Herança é o mecanismo do padrão, não um objetivo.
Quando usar × quando evitar
Use quando
3- Você não sabe de antemão os tipos exatos de objetos que o código vai precisar criar.
- Quer permitir que usuários da sua biblioteca estendam os componentes internos.
- Quer isolar a lógica de construção do resto da aplicação.
Evite quando
2- Só existe um tipo de produto e não há previsão de variação — vira complexidade inútil.
- Uma simples função de criação ou injeção de dependência já resolve.
Testar o que ficou
3 armadilhas de Factory Method viram perguntas, com o nome do padrão escondido, misturadas com padrões parecidos.
Onde isto aparece de verdade
Você já usa isto, talvez sem saber o nome.
document.createElementVocê pede um elemento pelo nome e recebe a subclasse certa de HTMLElement, sem instanciar nenhuma delas.React.createElementO JSX compila para uma chamada de fábrica: você descreve o que quer, e o React decide o que construir.Array.fromUm único ponto de criação que aceita fontes muito diferentes e devolve sempre um array.
O padrão em uma linha
Sem nome de domínio, sem cerimônia. Só a ideia.
// Subclasse decide qual produto criar.
criarBotao(): Botao { return new BotaoMac(); }O que ele cobra
1 salto a mais entre a chamada e o efeito.
- Uma classe e uma hierarquia a mais só para adiar a escolha do tipo concreto
- Cada produto novo tende a exigir uma subclasse de fábrica correspondente
Não vale se: só existe um tipo concreto e nada indica que vão surgir outros — aí new direto é mais honesto que a cerimônia.