Pular para o conteúdo
Criacionais

Factory Method

Define uma interface para criar um objeto, mas deixa as subclasses decidirem qual classe instanciar. Factory Method permite que uma classe adie a instanciação para suas subclasses.

Intenção

Definir um método em uma classe base que retorna um objeto compatível com uma interface comum, mas adiar a decisão de qual classe concreta instanciar para as subclasses. Isso permite que um framework funcione com qualquer tipo de produto definido pelo usuário sem modificação.

Problema

Uma classe precisa criar objetos, mas não pode antecipar o tipo exato de objeto que deve criar. Fixar o nome de uma classe específica no código acopla o criador àquele produto em particular, tornando impossível estender o sistema com novos tipos de produto sem modificar código existente. Você precisa de uma forma de delegar a decisão de 'qual classe?' para um ponto que possa ser sobrescrito.

Solução

Substitua chamadas diretas ao construtor por chamadas a um método de fábrica especial. A classe base declara o método de fábrica (geralmente abstrato), e cada subclasse o sobrescreve para retornar uma variante diferente do produto. O código cliente chama o método de fábrica através da interface da classe base, permanecendo desacoplado dos tipos concretos de produto.

Participantes

  • Creator — declara o método de fábrica (que pode ter uma implementação padrão) e o usa para obter instâncias de produto
  • ConcreteCreator — sobrescreve o método de fábrica para retornar um ConcreteProduct específico
  • Product — define a interface dos objetos que o método de fábrica cria
  • ConcreteProduct — implementa a interface Product

Vantagens

  • Desacopla o criador das classes concretas de produto (Princípio da Inversão de Dependência)
  • Novos tipos de produto podem ser introduzidos sem alterar o código existente do criador (Princípio Aberto/Fechado)
  • Centraliza a lógica de criação de produtos em um único lugar, facilitando controlar e trocar implementações
  • Suporta naturalmente o princípio de 'programar para uma interface'

Desvantagens

  • Exige criar uma nova subclasse do criador para cada novo tipo de produto, o que pode levar a uma hierarquia de classes paralela
  • Adiciona uma camada de indireção que pode dificultar o entendimento do código em casos simples
  • Se o criador tem lógica significativa além da criação de produtos, subclassificar só por causa do método de fábrica pode parecer excessivo

Analogia do mundo real

Uma empresa de logística tem um escritório central de despacho (o criador) que agenda entregas. O escritório não decide se vai usar um caminhão ou um navio — essa decisão é tomada por subescritórios regionais (criadores concretos) que conhecem as condições locais. O escritório central só sabe que vai receber um 'transporte' capaz de entregar carga. Subescritórios rurais retornam caminhões; subescritórios costeiros retornam navios.

Casos de uso

  • Código de framework que permite que subclasses no nível da aplicação controlem quais objetos criar
  • Editores de documento onde cada tipo de aplicação (editor de texto, planilha) cria sua própria subclasse de documento
  • Sistemas de logística onde o tipo de transporte é determinado pela região ou tipo de carga
  • Sistemas de plugin onde módulos de terceiros registram seus próprios criadores
  • Serviços de notificação que produzem e-mail, SMS, ou push notification dependendo da configuração

Exemplos de código

LogisticsCompany.java
interface Transport {
    String deliver(String cargo);
    int capacity();
}

final class Truck implements Transport {
    @Override
    public String deliver(String cargo) {
        return "Delivering "" + cargo + "" by road";
    }

    @Override
    public int capacity() {
        return 10; // tons
    }
}

final class Ship implements Transport {
    @Override
    public String deliver(String cargo) {
        return "Delivering "" + cargo + "" by sea";
    }

    @Override
    public int capacity() {
        return 500; // tons
    }
}

abstract class LogisticsCompany {
    // Factory method -- subclasses decide which Transport to create
    protected abstract Transport createTransport();

    public String planDelivery(String cargo) {
        Transport transport = createTransport();
        return transport.deliver(cargo) + " (capacity: " + transport.capacity() + "t)";
    }
}

final class RoadLogistics extends LogisticsCompany {
    @Override
    protected Transport createTransport() {
        return new Truck();
    }
}

final class SeaLogistics extends LogisticsCompany {
    @Override
    protected Transport createTransport() {
        return new Ship();
    }
}

public class FactoryMethodDemo {
    public static void main(String[] args) {
        LogisticsCompany company = new SeaLogistics();
        System.out.println(company.planDelivery("Electronics"));
    }
}

Classe criadora abstrata para um sistema de logística. Cada criador concreto sobrescreve o método de fábrica para retornar o tipo de transporte apropriado, enquanto a lógica de planejamento compartilhada fica na classe base.