Назначение
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 |
Реализация
Проблема: Каким образом клиент получает конкретную реализацию абстрактной фабрики, не зная при этом её реального типа?
- Через 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