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.
Interface abstrata
Seção intitulada “Interface abstrata”A interface TechFactory define a “família” de produtos que uma fábrica concreta deve saber criar:
public interface TechFactory { String getName(); Engine createEngine(); Shield createShield(); Weapon createWeapon();}Engine,ShieldeWeaponsão produtos relacionados (componentes da nave)- Cada implementação concreta da fábrica produz um conjunto coerente desses produtos
Fábrica concreta humana
Seção intitulada “Fábrica concreta humana”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
Fábrica concreta alienígena
Seção intitulada “Fábrica concreta alienígena”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
Uso da Abstract Factory em Spaceship
Seção intitulada “Uso da Abstract Factory em Spaceship”A classe Spaceship recebe a fábrica abstrata no construtor:
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:
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 espalharnew HumanEngine()ounew AlienWeapon()pelo código.
Prototype: Spaceship.clone()
Seção intitulada “Prototype: Spaceship.clone()”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.
public Spaceship clone() { return new Spaceship(this); // usa o construtor de cópia}Implementação atual de clone()
Seção intitulada “Implementação atual de clone()”Atualmente, o método clone() está assim:
@Overridepublic Spaceship clone() { return new Spaceship(this.techFactory);}E uso em Main:
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 denave01, mas o clone em si não copia o estado (name,energy,cor); ele apenas reutiliza a mesmaTechFactory- 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 à
TechFactorya 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()ounew 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:
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
Spaceshipfunciona 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).
Como os Padrões se Relacionam neste Projeto
Seção intitulada “Como os Padrões se Relacionam neste Projeto”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ão | Onde aparece | Problema que resolve | Como seria sem o padrão |
|---|---|---|---|
| Abstract Factory | TechFactory, HumanTechFactory, AlienTechFactory | Criar famílias coerentes de componentes (motor, arma, escudo) para cada tecnologia | Spaceship e Main teriam vários if/switch com new Human.../new Alien... |
| Prototype | Spaceship.clone() | Criar novas naves baseadas em uma nave existente, reaproveitando a mesma tecnologia | Có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.