Design Patterns
Criacionais
Factory Method

Padrão de Projeto: Factory Method

Propósito

O Factory Method é um padrão criacional de projeto que fornece uma interface para criar objetos em uma superclasse, mas permite que as subclasses alterem o tipo de objetos que serão criados.

Problema

Imagine que você está criando uma aplicação de gerenciamento de logística. Inicialmente, a aplicação lida apenas com transporte de caminhões, e a maior parte do código está na classe Caminhão. Com o tempo, a aplicação se torna popular e você recebe solicitações para incorporar logística marítima. Adicionar uma nova classe Navio exigiria alterações em toda a base de código, resultando em um código sujo e cheio de condicionais.

Solução

O padrão Factory Method sugere substituir chamadas diretas de construção de objetos (usando o operador new) por chamadas para um método fábrica especial. As subclasses podem alterar a classe de objetos retornados pelo método fábrica, desde que todos os produtos implementem uma interface comum.

Estrutura

  1. Produto: Declara a interface, que é comum a todos os objetos que podem ser produzidos pelo criador e suas subclasses.
  2. Produtos Concretos: Implementações diferentes da interface do produto.
  3. Criador: Declara o método fábrica que retorna novos objetos produto.
  4. Criadores Concretos: Sobrescrevem o método fábrica base para retornar um tipo diferente de produto.

Exemplo em PHP

Interface Produto

interface Transporte {
    public function entregar();
}

Produtos Concretos

class Caminhão implements Transporte {
    public function entregar() {
        echo "Entrega por terra em uma caixa.\n";
    }
}
 
class Navio implements Transporte {
    public function entregar() {
        echo "Entrega por mar em um contêiner.\n";
    }
}

Criador

abstract class Logistica {
    // O método fábrica
    abstract public function criarTransporte(): Transporte;
 
    public function planearEntrega() {
        $transporte = $this->criarTransporte();
        $transporte->entregar();
    }
}

Criadores Concretos

class LogisticaRodoviaria extends Logistica {
    public function criarTransporte(): Transporte {
        return new Caminhão();
    }
}
 
class LogisticaMaritima extends Logistica {
    public function criarTransporte(): Transporte {
        return new Navio();
    }
}

Código Cliente

function clientCode(Logistica $logistica) {
    $logistica->planearEntrega();
}
 
echo "App: Planejamento de entrega por terra:\n";
clientCode(new LogisticaRodoviaria());
 
echo "\nApp: Planejamento de entrega por mar:\n";
clientCode(new LogisticaMaritima());

Exemplo em Node.js

Interface Produto

class Transporte {
    entregar() {
        throw new Error("Método 'entregar()' deve ser implementado.");
    }
}

Produtos Concretos

class Caminhao extends Transporte {
    entregar() {
        console.log("Entrega por terra em uma caixa.");
    }
}
 
class Navio extends Transporte {
    entregar() {
        console.log("Entrega por mar em um contêiner.");
    }
}

Criador

class Logistica {
    // O método fábrica
    criarTransporte() {
        throw new Error("Método 'criarTransporte()' deve ser implementado.");
    }
 
    planearEntrega() {
        const transporte = this.criarTransporte();
        transporte.entregar();
    }
}

Criadores Concretos

class LogisticaRodoviaria extends Logistica {
    criarTransporte() {
        return new Caminhao();
    }
}
 
class LogisticaMaritima extends Logistica {
    criarTransporte() {
        return new Navio();
    }
}

Código Cliente

function clientCode(logistica) {
    logistica.planearEntrega();
}
 
console.log("App: Planejamento de entrega por terra:");
clientCode(new LogisticaRodoviaria());
 
console.log("\nApp: Planejamento de entrega por mar:");
clientCode(new LogisticaMaritima());

Aplicabilidade

  • Use o Factory Method quando não souber de antemão os tipos e dependências exatas dos objetos com os quais seu código deve funcionar.
  • Use o Factory Method quando desejar fornecer aos usuários da sua biblioteca ou framework uma maneira de estender seus componentes internos.
  • Use o Factory Method quando deseja economizar recursos do sistema reutilizando objetos existentes em vez de recriá-los sempre.

Prós e Contras

Prós

  • Evita acoplamentos firmes entre o criador e os produtos concretos.
  • Princípio de responsabilidade única: facilita a manutenção do código.
  • Princípio aberto/fechado: permite introduzir novos tipos de produtos sem quebrar o código cliente existente.

Contras

  • Pode tornar o código mais complicado devido à introdução de muitas subclasses novas.

Relações com outros padrões

  • Muitos projetos começam usando o Factory Method e evoluem para o Abstract Factory, Prototype, ou Builder.
  • Classes Abstract Factory são quase sempre baseadas em um conjunto de métodos fábrica.
  • Você pode usar o Factory Method junto com o Iterator para permitir que uma coleção de subclasses retornem diferentes tipos de iteradores.
  • O Factory Method é uma especialização do Template Method.

Este guia fornece uma visão abrangente do padrão Factory Method, ilustrando sua aplicação com exemplos em PHP e Node.js. Ele é essencial para criar sistemas flexíveis e extensíveis, permitindo a criação de objetos de maneira desacoplada e seguindo os princípios de design orientado a objetos.