Files
akshatarora0013 9d6ae392b8 refactoring: issue#2376 (#2491)
Changed all the switch expression according to the JAVA 17.
2023-04-01 17:48:38 +03:00
..
2023-04-01 17:48:38 +03:00

title, categories, language, tag
title categories language tag
Abstract Factory Creational pt
Gang of Four

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

alt text

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

Padrões relacionados

Créditos