Pular para o conteúdo

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 new de classes específicas.

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.

O Factory Method propõe:

  1. Definir uma interface Produto (o que o cliente usa).
  2. Definir uma classe Creator (criador) que possui:
    • lógica principal (ex.: planejarEntrega());
    • um método fábrica (ex.: criarTransporte()), que retorna o Produto.
  3. 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.


  • 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”:

  1. planejar entrega
  2. criar um transporte
  3. 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.


public interface Transporte {
void entregar();
}
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");
}
}
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();
}
public class LogisticaRodoviaria extends Logistica {
@Override
protected Transporte criarTransporte() {
return new Caminhao();
}
}
public class LogisticaMaritima extends Logistica {
@Override
protected Transporte criarTransporte() {
return new Navio();
}
}
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 Transporte
    • LogisticaAerea extends Logistica
  • sem reescrever a lógica principal nem “caçar new no projeto”.

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).

  • 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.

  • 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.