Pular para o conteúdo
Criacionais

Abstract Factory

Fornece uma interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas.

Intenção

Definir uma interface abstrata para criar um conjunto de produtos relacionados (uma 'família'), para que o código cliente possa trabalhar com qualquer família de forma intercambiável. A fábrica concreta determina quais variantes específicas de produto são instanciadas.

Problema

Seu código precisa criar conjuntos de objetos relacionados — por exemplo, controles de UI que precisam pertencer todos ao mesmo tema visual, ou objetos de acesso a dados que precisam ter como alvo o mesmo engine de banco de dados. Se você instancia classes concretas diretamente, trocar para uma família diferente exige mudanças espalhadas por vários arquivos, e misturar produtos de famílias diferentes introduz bugs sutis.

Solução

Declare interfaces abstratas para cada produto distinto da família, depois declare uma interface Abstract Factory com um método de criação para cada produto. Para cada variante de família, implemente uma fábrica concreta que retorna produtos daquela família específica. O código cliente trabalha exclusivamente através das interfaces abstratas, então trocar a família inteira é uma mudança de uma única linha no ponto de composição.

Participantes

  • AbstractFactory — declara métodos de criação para cada produto abstrato da família
  • ConcreteFactory — implementa os métodos de criação para produzir produtos de uma variante/família específica
  • AbstractProduct — declara uma interface para um tipo de produto (ex: Button, Checkbox)
  • ConcreteProduct — implementa a interface de produto abstrato para uma variante específica (ex: DarkButton, LightCheckbox)
  • Client — usa apenas a fábrica abstrata e as interfaces de produto abstratas

Vantagens

  • Garante que produtos da mesma família sejam usados juntos, evitando combinações incompatíveis
  • Isola as classes concretas de produto do código cliente (Princípio da Inversão de Dependência)
  • Trocar uma família de produtos inteira exige mudar apenas a fábrica concreta (Princípio Aberto/Fechado)
  • Promove consistência entre produtos criados pela mesma fábrica

Desvantagens

  • Adicionar um novo produto à família exige mudar todas as interfaces de fábrica e todas as implementações concretas
  • Introduz muitas interfaces e classes, o que pode parecer superengenharia para famílias pequenas
  • Pode obscurecer o caminho do código — quem lê precisa percorrer várias camadas de abstração para ver qual produto é realmente criado

Analogia do mundo real

Uma loja de móveis é como uma abstract factory. Você escolhe um estilo — moderno, vitoriano, ou art déco — e a loja fornece um conjunto compatível de cadeira, sofá e mesa de centro naquele estilo. Você nunca mistura uma cadeira vitoriana com um sofá moderno porque cada seção da loja (fábrica concreta) só produz itens da sua própria família de estilo.

Casos de uso

  • Toolkit de UI que suporta múltiplos temas (claro/escuro) ou plataformas alvo (Windows/macOS/Linux)
  • Camada de acesso a banco de dados que atende múltiplos engines (PostgreSQL, MySQL, SQLite) com objetos compatíveis de conexão, comando e leitura
  • Renderização de documentos multiplataforma que produz famílias de saída consistentes em PDF, HTML, ou DOCX
  • Carregamento de assets de jogo que fornece conjuntos compatíveis de sprite, som e animação por tema de fase
  • Abstração de infraestrutura cloud que agrupa recursos compatíveis de computação, armazenamento e rede por provedor

Exemplos de código

UIFactory.java
interface Button {
    String render();
}

interface Checkbox {
    String render();
    void toggle();
}

interface UIFactory {
    Button createButton(String label);
    Checkbox createCheckbox(String label);
}

final class DarkButton implements Button {
    private final String label;
    DarkButton(String label) { this.label = label; }

    @Override
    public String render() { return "[Dark Button: " + label + "]"; }
}

final class DarkCheckbox implements Checkbox {
    private final String label;
    private boolean checked = false;
    DarkCheckbox(String label) { this.label = label; }

    @Override
    public String render() { return "[Dark Checkbox: " + label + " " + (checked ? "☑" : "☐") + "]"; }

    @Override
    public void toggle() { checked = !checked; }
}

final class LightButton implements Button {
    private final String label;
    LightButton(String label) { this.label = label; }

    @Override
    public String render() { return "[Light Button: " + label + "]"; }
}

final class LightCheckbox implements Checkbox {
    private final String label;
    private boolean checked = false;
    LightCheckbox(String label) { this.label = label; }

    @Override
    public String render() { return "[Light Checkbox: " + label + " " + (checked ? "☑" : "☐") + "]"; }

    @Override
    public void toggle() { checked = !checked; }
}

final class DarkThemeFactory implements UIFactory {
    @Override
    public Button createButton(String label) { return new DarkButton(label); }

    @Override
    public Checkbox createCheckbox(String label) { return new DarkCheckbox(label); }
}

final class LightThemeFactory implements UIFactory {
    @Override
    public Button createButton(String label) { return new LightButton(label); }

    @Override
    public Checkbox createCheckbox(String label) { return new LightCheckbox(label); }
}

public class AbstractFactoryDemo {
    public static void main(String[] args) {
        UIFactory factory = new DarkThemeFactory();
        Button button = factory.createButton("Save");
        Checkbox checkbox = factory.createCheckbox("Remember me");

        System.out.println(button.render());
        System.out.println(checkbox.render());
    }
}

Fábrica de temas de UI que produz widgets Button e Checkbox consistentes para temas claro e escuro. O código cliente depende apenas das interfaces abstratas e pode trocar de família de tema no ponto de composição.