Factory Method
Factory Method é um padrão criacional que resolve um problema comum: preciso criar objetos, mas não quero que meu código fique acoplado às classes concretas.
A ideia é simples:
- você escreve um método fábrica (factory method) para criar um “produto”;
- classes filhas podem decidir qual produto concreto criar;
- o código principal trabalha com interfaces/abstrações, não com
newde classes específicas.
Problema
Seção intitulada “Problema”Imagine um módulo de “logística” que precisa planejar entregas.
No começo, só existe entrega por caminhão:
public class Logistica { public void planejarEntrega() { Caminhao caminhao = new Caminhao(); caminhao.entregar(); }}
public class Caminhao { public void entregar() { System.out.println("Entrega por caminhão"); }}Funciona… até o sistema precisar suportar:
- entrega marítima (
Navio) - entrega aérea (
Aviao) - entrega por drones, etc.
Sem um padrão, a tendência é criar if/else/switch espalhado ou “trocar o new” por todo lado:
- alto acoplamento à implementação concreta;
- mudanças frequentes em código já testado;
- difícil estender sem introduzir bugs.
Solução
Seção intitulada “Solução”O Factory Method propõe:
- Definir uma interface Produto (o que o cliente usa).
- Definir uma classe Creator (criador) que possui:
- lógica principal (ex.:
planejarEntrega()); - um método fábrica (ex.:
criarTransporte()), que retorna o Produto.
- lógica principal (ex.:
- Subclasses do Creator sobrescrevem o método fábrica para devolver o Produto concreto adequado.
Assim, o Creator usa o produto via abstração, e a decisão de “qual classe concreta” fica isolada.
Estrutura (papéis)
Seção intitulada “Estrutura (papéis)”- Product: interface/classe base do objeto criado (
Transporte) - ConcreteProduct: implementações (
Caminhao,Navio) - Creator: define o factory method e usa o Product (
Logistica) - ConcreteCreator: sobrescreve o factory method (
LogisticaRodoviaria,LogisticaMaritima)
Analogia (Refactoring Guru): logística e transportes
Seção intitulada “Analogia (Refactoring Guru): logística e transportes”Pense em duas empresas:
- Uma faz entregas por estrada (caminhão).
- Outra faz entregas entre portos (navio).
Ambas seguem o mesmo “processo”:
- planejar entrega
- criar um transporte
- mandar o transporte entregar
A diferença está só em qual transporte é criado.
O Factory Method permite reaproveitar o processo e variar apenas a criação.
Exemplo completo em Java (didático)
Seção intitulada “Exemplo completo em Java (didático)”Product
Seção intitulada “Product”public interface Transporte { void entregar();}Concrete Products
Seção intitulada “Concrete Products”public class Caminhao implements Transporte { @Override public void entregar() { System.out.println("Entrega por caminhão"); }}
public class Navio implements Transporte { @Override public void entregar() { System.out.println("Entrega por navio"); }}Creator (com a lógica principal)
Seção intitulada “Creator (com a lógica principal)”public abstract class Logistica {
public void planejarEntrega() { Transporte transporte = criarTransporte(); // factory method // ...aqui poderia ter validações, cálculo de rota, custos, etc... transporte.entregar(); }
protected abstract Transporte criarTransporte();}Concrete Creators
Seção intitulada “Concrete Creators”public class LogisticaRodoviaria extends Logistica { @Override protected Transporte criarTransporte() { return new Caminhao(); }}
public class LogisticaMaritima extends Logistica { @Override protected Transporte criarTransporte() { return new Navio(); }}Uso (cliente)
Seção intitulada “Uso (cliente)”public class App { public static void main(String[] args) { Logistica log1 = new LogisticaRodoviaria(); log1.planejarEntrega();
Logistica log2 = new LogisticaMaritima(); log2.planejarEntrega(); }}Perceba o ponto importante:
planejarEntrega()não muda.- para adicionar um novo transporte (ex.:
Aviao), você cria:Aviao implements TransporteLogisticaAerea extends Logistica
- sem reescrever a lógica principal nem “caçar
newno projeto”.
Aplicabilidade (quando usar)
Seção intitulada “Aplicabilidade (quando usar)”Use Factory Method quando:
- Uma classe não deve conhecer quais classes concretas ela instancia.
- Você quer permitir que o sistema seja estendido com novos tipos sem mexer no fluxo principal.
- A criação do objeto varia conforme:
- ambiente (dev/prod),
- tipo de entrada,
- regras de negócio,
- região/tenant/cliente, etc.
Exemplos comuns:
- escolher um
Parser(JsonParser,XmlParser) - escolher um
Notificador(Email,SMS,Push) - escolher um
ClienteHttp(mock vs real; Apache vs OkHttp)
- Menor acoplamento: o cliente depende de abstrações (
Transporte), não de concretos. - Mais fácil estender: novos produtos entram com novas subclasses (sem “mexer no que funciona”).
- Centraliza a criação: você controla “onde nasce” cada objeto.
- Ajuda a aplicar OCP (Open/Closed).
Contras
Seção intitulada “Contras”- Mais classes: Creator/ConcreteCreator + Products.
- Pode ser overengineering para casos muito simples.
- Se você exagerar, pode virar uma floresta de fábricas sem necessidade real.
Relações com outros padrões
Seção intitulada “Relações com outros padrões”- Abstract Factory: cria famílias de objetos relacionados; Factory Method cria um produto por vez.
- Template Method: o
planejarEntrega()lembra um “template” e o factory method é um ponto de variação. - Builder: Builder monta objetos complexos passo a passo; Factory Method cria objetos “de uma vez”.
- Factory Method = “deixe subclasses decidirem o que instanciar”.
- Objetivo principal: desacoplar criação do uso.
- Ganho prático: extensão com menos risco, mantendo o fluxo principal estável.