Changed all the switch expression according to the JAVA 17.
title, categories, language, tag
| title | categories | language | tag | |
|---|---|---|---|---|
| Abstract Factory | Creational | pt |
|
Também conhecido como
Kit
Propósito
Fornece uma interface para criar famílias de objetos relacionados dependentes sem especificar sua classe concreta.
Explicação
Exemplo do Mundo Real
Para criar um reino precisamos de objetos com um tema comum. O reino élfico precisa de um rei élfico, um castelo élfico e um exército élfico, enquanto o reino orc precisa de um rei orc, um castelo orc e um exército orc. Existe uma dependência entre os objetos do reino.
Em outras palavras
Uma fábrica de fábricas; uma fábrica que agrupa outras fábricas individuais, mas relacionadas/dependentes, sem especificar sua classe concreta.
De acordo com a Wikipédia
A essência do padrão Abstract Factory é fornecer uma interface para criar famílias de objetos relacionados ou dependentes sem especificar suas classes concretas.
Exemplo Programático
Traduzindo o exemplo do reino acima. Em primeiro lugar, temos algumas interfaces e implementação para os objetos no reino.
public interface Castle {
String getDescription();
}
public interface King {
String getDescription();
}
public interface Army {
String getDescription();
}
// Elven implementations ->
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the elven castle!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfKing implements King {
static final String DESCRIPTION = "This is the elven king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the elven Army!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
// Orcish implementations similarly -> ...
Em seguida, temos a abstração e as implementações para a fábrica do reino.
public interface KingdomFactory {
Castle createCastle();
King createKing();
Army createArmy();
}
public class ElfKingdomFactory implements KingdomFactory {
@Override
public Castle createCastle() {
return new ElfCastle();
}
@Override
public King createKing() {
return new ElfKing();
}
@Override
public Army createArmy() {
return new ElfArmy();
}
}
public class OrcKingdomFactory implements KingdomFactory {
@Override
public Castle createCastle() {
return new OrcCastle();
}
@Override
public King createKing() {
return new OrcKing();
}
@Override
public Army createArmy() {
return new OrcArmy();
}
}
Agora temos a fábrica abstrata que nos permite fazer uma família de objetos relacionados, ou seja, a fábrica do reino élfico cria castelo élfico, rei e exército, etc.
var factory = new ElfKingdomFactory();
var castle = factory.createCastle();
var king = factory.createKing();
var army = factory.createArmy();
castle.getDescription();
king.getDescription();
army.getDescription();
Output do programa:
This is the elven castle!
This is the elven king!
This is the elven Army!
Agora, podemos projetar uma fábrica para nossas diferentes fábricas do reino. Neste exemplo, criamos o FactoryMaker, responsável por devolver uma instância de ElfKingdomFactory ou OrcKingdomFactory.
O cliente pode usar o FactoryMaker para criar uma fatoração concreta, que uma vez, produzirá diferentes objetos concretos (derivados de Army, King, Castle).
Neste exemplo, também usamos um enum para parametrizar qual tipo de fábrica do reino o cliente solicitará.
public static class FactoryMaker {
public enum KingdomType {
ELF, ORC
}
public static KingdomFactory makeFactory(KingdomType type) {
return switch (type) {
case ELF -> new ElfKingdomFactory();
case ORC -> new OrcKingdomFactory();
default -> throw new IllegalArgumentException("KingdomType not supported.");
};
}
}
public static void main(String[] args) {
var app = new App();
LOGGER.info("Elf Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
LOGGER.info(app.getArmy().getDescription());
LOGGER.info(app.getCastle().getDescription());
LOGGER.info(app.getKing().getDescription());
LOGGER.info("Orc Kingdom");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
--similar use of the orc factory
}
Diagrama de classes
Aplicabilidade
Use o padrão Abstract Factory quando
- O sistema deve ser independente de como seus produtos são criados, compostos e representados
- O sistema deve ser configurado com uma das várias famílias de produtos
- A família de objetos de produtos relacionados foi projetada para ser usada em conjunto e você precisa aplicar essa restrição
- Você deseja fornecer uma biblioteca de classes de produtos e deseja revelar apenas suas interfaces, não suas implementações
- O tempo de vida da dependência é conceitualmente menor que o tempo de vida do consumidor.
- Você precisa de um valor em tempo de execução para construir uma dependência específica
- Você deseja decidir qual produto chamar de uma família em tempo de execução.
- Você precisa fornecer um ou mais parâmetros conhecidos apenas em tempo de execução antes de resolver uma dependência.
- Quando você precisa de consistência entre os produtos
- Você não deseja alterar o código existente ao adicionar novos produtos ou famílias de produtos ao programa.
Exemplos de casos de uso
- Selecionando para chamar a implementação apropriada de FileSystemAcmeService ou DatabaseAcmeService ou NetworkAcmeService em tempo de execução.
- A escrita de casos de teste de unidade torna-se muito mais fácil
- Ferramentas de interface do usuário para diferentes sistemas operacionais
Consequencias
- A injeção de dependência em java oculta as dependências da classe de serviço que podem levar a erros de tempo de execução que seriam detectados em tempo de compilação.
- Embora o padrão seja ótimo ao criar objetos predefinidos, adicionar novos pode ser um desafio.
- O código se torna mais complicado do que deveria, pois muitas novas interfaces e classes são introduzidas junto com o padrão.
Tutoriais
Usos conhecidos
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.transform.TransformerFactory
- javax.xml.xpath.XPathFactory
