Implementação do Padrão Factory Method em Java

Conceito Fundamental

O Factory Method (Método Fábrica) é um padrão criacional que define uma assinatura para a construção de objetos, delegando às subclasses a responsabilidade de escohler qual classe concreta será materializada. Essa estratégia posterga a vinculação direta com tipos específicos até a execução, permitindo que a estrutura principal permaneça estável enquanto novas variantes são introduzidas.

Diferente de uma fábrica monolítica que concentra todas as regras de instanciação, este modelo fragmenta a criação. Cada subclasse da fábrica assume a obrigação de proudzir um único tipo de derivado, alinhando-se diretamente ao princípio da responsabilidade única e facilitando a manutenção do código.

Componentes da Arquitetura

  • Produto (Produto): Interface ou classe abstrata que estabelece o contrato comportamental dos objetos gerados pelo método fábrica.
  • ProdutoConcreto: Implementações específicas que cumprem a interface definida no contrato de Produto.
  • Criador (Criador): Classe abstrata ou interface que declara o método responsável pela instanciação. Pode conter métodos auxiliares que operam sobre o produto retornado, mantendo a lógica de fluxo de controle desacoplada dos detalhes de criação.
  • CriadorConcreto: Subclasses que herdam ou implementam o Criador, sobrescrevendo o método fábrica para retornar a instância adequada do ProdutoConcreto correspondente.

Representação UML

classDiagram
    class Produto {
        <<Interface>>
        +executarOperacao()
    }
    class ProdutoConcretoA {
        +executarOperacao()
    }
    class ProdutoConcretoB {
        +executarOperacao()
    }
    Produto <|.. ProdutoConcretoA
    Produto <|.. ProdutoConcretoB

    class Criador {
        <<Abstract>>
        +instanciarProduto(): Produto
        +processarFluxo()
    }
    class CriadorConcretoA {
        +instanciarProduto(): Produto
    }
    class CriadorConcretoB {
        +instanciarProduto(): Produto
    }
    Criador <|-- CriadorConcretoA
    Criador <|-- CriadorConcretoB
    CriadorConcretoA ..> ProdutoConcretoA : cria
    CriadorConcretoB ..> ProdutoConcretoB : cria

    Cliente --> Criador
    Cliente --> Produto

O método processarFluxo() na classe abstrata Criador tipicamente invoca instanciarProduto() para obter uma referência válida e, em seguida, executa as operações de domínio necessárias sobre esse objeto.

Análise de Vantagens e Limitações

Pontos Positivos:

  • Conformidade com o Princípio Aberto/Fechado: A introdução de novas variantes exige apenas a criação de novas classes concretas, sem modificar a infraestrutura existente.
  • Desacoplamento Efetivo: A lógica de consumo de objetos permanece independente dos mecanismos de instanciação. O cliente manipula apenas contratos abstratos.
  • Extensibilidade Controlada: Subclasses podem redefinir o tipo de objeto retornado, garantindo que cada fábrica gerencie um domínio específico e previsível.

Restrições:

  • Proliferação de Classes: Cada nova variante demanda a implementação de um produto concreto e sua respectiva fábrica, aumentando o volume de arquivos e a complexidade do projeto em cenários simples.

Contextos de Aplicação

  • Quando uma classe desconhece antecipadamente os tipos exatos dos objetos que deve manipular.
  • Quando a política de criação deve ser delegada e controlada por subclasses especializadas.
  • Quando se deseja encapsular a seleção de implementações alternativas em módulos auxiliares, localizando a lógica de decisão.

Exemplo Prático em Java

O cenário abaixo modela um sistema de conversão de relatórios financeiros, onde diferentes formatos de saída (PDF ou CSV) são gerados dinamicamente com base na escolha do cliente.

public interface ExportadorFinanceiro {
    String converter(Map<String, Double> dados);
    String obterExtensao();
}

public class ExportadorPdf implements ExportadorFinanceiro {
    @Override
    public String converter(Map<String, Double> dados) {
        StringBuilder buffer = new StringBuilder("%PDF-1.4\n%Relatorio Gerado\n");
        dados.forEach((k, v) -> buffer.append(k).append(": ").append(v).append("\n"));
        return buffer.toString();
    }

    @Override
    public String obterExtensao() {
        return ".pdf";
    }
}

public class ExportadorCsv implements ExportadorFinanceiro {
    @Override
    public String converter(Map<String, Double> dados) {
        StringBuilder buffer = new StringBuilder("Chave,Valor\n");
        dados.forEach((k, v) -> buffer.append(k).append(",").append(v).append("\n"));
        return buffer.toString();
    }

    @Override
    public String obterExtensao() {
        return ".csv";
    }
}

public abstract class GeradorRelatorio {
    public abstract ExportadorFinanceiro selecionarExportador();

    public void executarExportacao(String titulo, Map<String, Double> valores) {
        ExportadorFinanceiro exportador = selecionarExportador();
        System.out.println("Inicializando exportação [" + exportador.obterExtensao() + "]");
        
        String conteudo = exportador.converter(valores);
        System.out.println("Conteúdo gerado para: " + titulo);
        System.out.println(conteudo.split("\n")[0]); // Apenas cabeçalho para demonstração
    }
}

public class GeradorPdf extends GeradorRelatorio {
    @Override
    public ExportadorFinanceiro selecionarExportador() {
        System.out.println("Alocando recursos para PDF...");
        return new ExportadorPdf();
    }
}

public class GeradorCsv extends GeradorRelatorio {
    @Override
    public ExportadorFinanceiro selecionarExportador() {
        System.out.println("Preparando estrutura CSV...");
        return new ExportadorCsv();
    }
}

Implementação do consumidor:

import java.util.HashMap;
import java.util.Map;

public class Aplicacao {
    public static void main(String[] args) {
        Map<String, Double> balanco = new HashMap<>();
        balanco.put("Receita", 50000.00);
        balanco.put("Despesas", 12000.00);

        GeradorRelatorio fabricaPdf = new GeradorPdf();
        fabricaPdf.executarExportacao("Balanco_Mensal", balanco);

        System.out.println("---\n");

        GeradorRelatorio fabricaCsv = new GeradorCsv();
        fabricaCsv.executarExportacao("Balanco_Mensal_Detalhado", balanco);
        
        // Para adicionar um novo formato (ex: XML), basta criar:
        // 1. ExportadorXml implementando ExportadorFinanceiro
        // 2. GeradorXml estendendo GeradorRelatorio
        // A aplicação cliente permanece inalterada.
    }
}

Saída gerada durante a execução:

Alocando recursos para PDF...
Inicializando exportação [.pdf]
Conteúdo gerado para: Balanco_Mensal
%PDF-1.4
---
Preparando estrutura CSV...
Inicializando exportação [.csv]
Conteúdo gerado para: Balanco_Mensal_Detalhado
Chave,Valor

Tags: factory-method-pattern java-core orientação-a-objetos arquitetura-de-software padroes-criacionais

Publicado em 8-18 02:19