Abstract Factory

Назначение

Abstract Factory обеспечивает создание семейств взаимосвязанных или зависящих друг от друга объектов без указания конкретных классов. Этим объектам соответствует набор связанных интерфейсов, которые реализуют создаваемые объекты.

  • клиент не должен зависеть от способа получения продукта;
  • порождаемые продукты создаются в виде определенного набора, то есть реализуют интерфейсы, некоторым образом связанные между собой.

Описание

Допустим, необходимо создавать продукты двух (или более) типов, которые определяются интерфейсами ProductA, ProductB. Эти интерфейсы каким-либо образом связаны друг с другом (например, это элементы одной оконной системы: текстовое поле и кнопка для Windows, Linux, MacOS; или это юниты одной армии: лучник и рыцарь японцев, немцев или римлян). Для создания этих продуктов определен интерфейс Factory в котором имеется отдельный метод для создания продукта каждого из типов: createA() и createB(). Для создания конкретных продуктов есть конкретные реализации 1 и 2 интерфейса Factory: ConcreteFactory1, ConcreteFactory2, которые производят соответствующие конкретные продукты указанных интерфейсов:

Производитель Метод Продукт
ConcreteFactory1 createA()
createB()
ConcreteProductA1
ConcreteProductB1
ConcreteFactory2 createA()
createB()
ConcreteProductA2
ConcreteProductB2

Реализация

Factory
Connection
Интерфейс фабрики, определяет набор взаимосвязанных абстрактных продуктов
ProductA, ProductB
Statement, PreparedStatement, CallableStatement
Интерфейсы, которые описывают общие функции каждого из продуктов, они взаимосвязаны между собой и пораждаются одной фабрикой
createA(), createB()
createStatement(), prepareStatement(), prepareCall()
Методы абстрактной фабрики для получения соответствующего вида продукта
ConcreteFactory1, ConcreteFactory2
PgConnection, OracleConnection
Классы, реализующие функционал абстрактной фабрики для набора конкретных продуктов одного вида
ConcreteProductA1, ConcreteProductB1 и ConcreteProductA2, ConcreteProductB2
PgStatement, PgPreparedStatement, PgCallableStatement
Связанные между собой конкретные продукты определенного вида

Проблема: Каким образом клиент получает конкретную реализацию абстрактной фабрики, не зная при этом её реального типа?

  • Через DI фреймворк, например, Spring создает bean конкретной фабрики в зависимости от конфигурации
  • Кто-то, кто обладает информацией о том, какая должна быть конкретная фабрика (например, по текущей конфигурации или в зависимости от контекста), устанавливает её клиенту через setter/конструктор
  • Через шаблон Factory Method, который по типу аргументов принимает решение о создании той или иной реализации фабрики. Например, в java-design-patterns это сделано следующим образом:
public static class FactoryMaker {

  public enum KingdomType {
    ELF, ORC
  }

  public static KingdomFactory makeFactory(KingdomType type) {
    switch (type) {
      case ELF:
        return new ElfKingdomFactory();
      case ORC:
        return new OrcKingdomFactory();
      default:
        throw new IllegalArgumentException("KingdomType not supported.");
    }
  }
}

Примеры

  • java.sql.Connection - пример абстрактной фабрики, которую можно получить Connection connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/database");

Чем отличается

Factory method - Mark Grand производит один продукт в то время как Abstract Factory производит семейство объектов, которые связаны между собой. Структурно Abstract Factory является обобщением Factory method, если рассматривать интерпретацию Mark Grand и подход “Determination by configuration” когда у производящего метода нет параметров. В подходе “Data-driven class determination” Factory Method немного отличается от Abstract Factory тем, что реализация интерфейса фабрики всего одна и в этой реализации инкапсулирована логика определения типа продуктов.

Ссылки

https://java-design-patterns.com/patterns/abstract-factory/

https://github.com/iluwatar/java-design-patterns/tree/master/abstract-factory

https://refactoring.guru/design-patterns/abstract-factory

What are the differences between abstract factory and factory design patterns

Baeldung - Abstract Factory Pattern in Java