Pular para o conteúdo

Exemplo 02 - Prototype e Abstract Factory

O projeto demonstra a aplicação dos padrões de projeto Abstract Factory e Prototype em um cenário de naves espaciais que podem usar diferentes tecnologias (humana ou alienígena).


Abstract Factory: TechFactory, HumanTechFactory e AlienTechFactory

Seção intitulada “Abstract Factory: TechFactory, HumanTechFactory e AlienTechFactory”

O padrão Abstract Factory fornece uma interface para criação de famílias de objetos relacionados sem expor suas classes concretas.

A interface TechFactory define a “família” de produtos que uma fábrica concreta deve saber criar:

TechFactory.java
public interface TechFactory {
String getName();
Engine createEngine();
Shield createShield();
Weapon createWeapon();
}
  • Engine, Shield e Weapon são produtos relacionados (componentes da nave)
  • Cada implementação concreta da fábrica produz um conjunto coerente desses produtos
HumanTechFactory.java
public class HumanTechFactory implements TechFactory {
@Override
public String getName() {
return "Tecnologia Humana";
}
@Override
public Engine createEngine() {
return new HumanEngine();
}
@Override
public Weapon createWeapon() {
return new HumanWeapon();
}
@Override
public Shield createShield() {
return new HumanShield();
}
}
  • Gera sempre componentes da “família humana”:
    HumanEngine,
    HumanWeapon,
    HumanShield
AlienTechFactory.java
public class AlienTechFactory implements TechFactory {
@Override
public String getName() {
return "Tecnologia Alienígena";
}
@Override
public Engine createEngine() {
return new AlienEngine();
}
@Override
public Weapon createWeapon() {
return new AlienWeapon();
}
@Override
public Shield createShield() {
return new AlienShield();
}
}
  • Gera sempre componentes da “família alienígena”:
    AlienEngine,
    AlienWeapon,
    AlienShield

A classe Spaceship recebe a fábrica abstrata no construtor:

Spaceship.java
public class Spaceship {
private String name = "Nave Genérica";
private Integer energy = 10;
private String cor = "Cinza";
private final Engine engine;
private final Weapon weapon;
private final Shield shield;
private final TechFactory techFactory;
public Spaceship(TechFactory techFactory) {
this.techFactory = techFactory;
this.engine = techFactory.createEngine();
this.weapon = techFactory.createWeapon();
this.shield = techFactory.createShield();
}
// ...
}
  • A nave não sabe se a tecnologia é humana ou alienígena; ela só usa a interface TechFactory
  • A consistência da família (todos os componentes humanos ou todos alienígenas) é garantida pela fábrica concreta

O cliente seleciona a fábrica no Main:

Main.java
public class Main {
public static void main(String[] args) {
TechFactory tecnologiaHumana = new HumanTechFactory();
Spaceship nave01 = new Spaceship(tecnologiaHumana);
// ...
TechFactory tecnologiaAlienigena = new AlienTechFactory();
Spaceship nave03 = new Spaceship(tecnologiaAlienigena);
// ...
}
}
  • Ao trocar apenas a implementação de TechFactory, cria-se uma nave completa com outra família de componentes
  • O código cliente não precisa conhecer HumanEngine, AlienShield, etc.

Problema que resolve:

Criar famílias de objetos relacionados (Engine, Weapon, Shield) trocando apenas a fábrica, sem espalhar new HumanEngine() ou new AlienWeapon() pelo código.


O padrão Prototype define a criação de novos objetos a partir da cópia de um objeto existente (protótipo), em vez de criar a partir de new e de uma construção tradicional.

Neste projeto, o comportamento de “clonar” está na própria Spaceship.

Spaceship.java
public Spaceship clone() {
return new Spaceship(this); // usa o construtor de cópia
}

Atualmente, o método clone() está assim:

Spaceship.java
@Override
public Spaceship clone() {
return new Spaceship(this.techFactory);
}

E uso em Main:

Main.java
Spaceship nave01 = new Spaceship(tecnologiaHumana);
nave01.setName("Explorador I");
nave01.setEnergy(100);
nave01.showInfo();
Spaceship nave02 = nave01.clone();
nave02.setName("Explorador II");
nave02.showInfo();
  • nave02 é criada a partir de nave01, mas o clone em si não copia o estado (name, energy, cor); ele apenas reutiliza a mesma TechFactory
  • Depois do clone, o cliente ajusta manualmente o estado: setName("Explorador II")

Ou seja:

  • A nave original (nave01) atua como protótipo conceitual: a nova nave é criada “do mesmo tipo de tecnologia”
  • A implementação mistura Prototype com Abstract Factory: o clone delega à TechFactory a criação de novos componentes (engine, weapon, shield)

Problema que resolve:

Criar rapidamente uma nova nave do mesmo “tipo tecnológico” da nave original, sem precisar decidir de novo se é humana ou alienígena e sem precisar chamar explicitamente new HumanTechFactory() ou new AlienTechFactory().

Versão alternativa (Clone por construtor de cópia)

Seção intitulada “Versão alternativa (Clone por construtor de cópia)”

O código comentado no exemplo mostra uma implementação direta de Prototype via construtor de cópia:

Spaceship.java
public Spaceship(Spaceship clone) {
this.name = clone.name;
this.energy = clone.energy;
this.techFactory = clone.techFactory;
this.engine = clone.engine;
this.weapon = clone.weapon;
this.shield = clone.shield;
}
  • Essa versão efetivamente copia os atributos do objeto existente para o novo
  • Spaceship funciona como protótipo: o novo objeto nasce com o mesmo estado (nome, energia, componentes)
  • Essa é a versão mais indicada para um Prototype “canônico”, onde o clone é uma cópia completa do protótipo, e o cliente pode modificar o estado depois do clone, mas não precisa se preocupar com a criação dos componentes (que já estão copiados).

Relação entre as classes principais:

TechFactory (Abstract Factory)
├── HumanTechFactory
│ ├── HumanEngine (Engine)
│ ├── HumanWeapon (Weapon)
│ └── HumanShield (Shield)
└── AlienTechFactory
├── AlienEngine (Engine)
├── AlienWeapon (Weapon)
└── AlienShield (Shield)
Spaceship
├── Engine
├── Weapon
├── Shield
└── TechFactory ← fornecida pelas fábricas concretas
Spaceship.clone() (Prototype-like)
└── cria nova Spaceship usando a mesma TechFactory do protótipo
PadrãoOnde apareceProblema que resolveComo seria sem o padrão
Abstract FactoryTechFactory, HumanTechFactory, AlienTechFactoryCriar famílias coerentes de componentes (motor, arma, escudo) para cada tecnologiaSpaceship e Main teriam vários if/switch com new Human.../new Alien...
PrototypeSpaceship.clone()Criar novas naves baseadas em uma nave existente, reaproveitando a mesma tecnologiaCódigo cliente teria que decidir de novo qual fábrica usar e como construir tudo

Se desejar aproximar ainda mais do Prototype “canônico”, pode-se reativar o construtor de cópia em Spaceship e alterar o clone() para usar esse construtor, copiando o estado completo da nave protótipo.