* Translate Abstract Factory to Russian * Fix a typo
title, category, language, tag
| title | category | language | tag | |
|---|---|---|---|---|
| Abstract Factory | Creational | ru |
|
Альтернативные названия
Kit
Цель
Предоставление интерфейса для создания семейств взаимосвязанных или взаимозависимых объектов без указания их конкретных классов.
Объяснение
Пример из реального мира:
Представьте, что вы хотите создать королевство с замком, королём и армией. Эльфийскому королевству понадобится эльфийский замок, эльфийский король и эльфийская армия, в то время как оркскому королевству понадобится оркский замок, оркский король и оркская армия. Между объектами королевства существует взаимозависимость.
Простыми словами:
Фабрика фабрик; фабрика, которая объединяет отдельные, но взаимозависимые/взаимозаменяемые фабрики без указания их конкретных классов.
Википедия пишет:
Порождающий шаблон проектирования, предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов.
Программный пример
Основываясь на примере королевства выше, во-первых, нам понадобятся интерфейсы и реализации для объектов в королевстве.
public interface Castle {
String getDescription();
}
public interface King {
String getDescription();
}
public interface Army {
String getDescription();
}
// Эльфийская реализация
public class ElfCastle implements Castle {
static final String DESCRIPTION = "Это эльфийский замок!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfKing implements King {
static final String DESCRIPTION = "Это эльфийский король!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfArmy implements Army {
static final String DESCRIPTION = "Это эльфийская армия!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
// Оркская реализация
public class OrcCastle implements Castle {
static final String DESCRIPTION = "Это оркский замок!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class OrcKing implements King {
static final String DESCRIPTION = "Это оркский король!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class OrcArmy implements Army {
static final String DESCRIPTION = "Это оркская армия!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
Во-вторых, нам понадобятся абстракция фабрики королевства, а также реализации этой абстракции:
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();
}
}
На данный момент, у нас есть абстрактная фабрика, которая позволяет создавать семейство взаимосвязанных объектов, то есть фабрика эльфийского королевства создаёт эльфийский замок, эльфийского короля, эльфийскую армию и так далее:
var factory = new ElfKingdomFactory();
var castle = factory.createCastle();
var king = factory.createKing();
var army = factory.createArmy();
castle.getDescription();
king.getDescription();
army.getDescription();
Вывод программы:
Это эльфийский замок!
Это эльфийский король!
Это эльфийская армия!
Можно спроектировать фабрику для наших различных фабрик королевств. В следующем примере, мы создали FactoryMaker, который ответственен за создание экземпляра ElfKingdomFactory, либо OrcKingdomFactory.
Клиент может использовать класс FactoryMaker, чтобы создать желаемую конкретную фабрику, которая в свою очередь будет производить разные конкретные объекты, унаследованные от Castle, King, Army.
В этом примере использовано перечисление, чтобы параметризовать то, какую фабрику королевства запрашивает клиент:
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 -> throe new IllegalArgumentException("Данный тип королества не поддерживается.")
}
}
}
public static void main(String[] args) {
var app = new App();
LOGGER.info("Эльфийское королевство");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF));
LOGGER.indo(app.getCastle().getDescription());
LOGGER.indo(app.getKinv().getDescription());
LOGGER.indo(app.getArmy().getDescription());
LOGGER.info("Оркское королевство");
app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC));
LOGGER.indo(app.getCastle().getDescription());
LOGGER.indo(app.getKinv().getDescription());
LOGGER.indo(app.getArmy().getDescription());
}
Диаграмма классов
Применимость
Используйте шаблон проектирования абстрактная фабрика, когда:
- Система должна быть независимой от того, как создаются, составляются и представляются её продукты;
- Система должна быть сконфигурирована одним из нескольких семейств продуктов;
- Семейство связанных продуктов спроектировано для совместного использования и вам необходимо обеспечить соблюдение данного ограничения;
- Вы хотите предоставить библиотеку классов, но готовы раскрыть только их интерфейсы, но не реализацию;
- Время жизни зависимости концептуально короче, чем время жизни потребителя;
- Вам требуется значение времени исполнения, чтобы создать требуемую зависимость;
- Вы хотите решить какие продукты из семейства вам нужны во время исполнения;
- Вам нужно предоставить один или несколько параметров, которые известны только во время исполнения прежде, чем вы сможете решить зависимость;
- В случае, когда требуется последовательность среди продуктов;
- Вы не хотите менять существующий код, когда добавляются новые продукты или семейство продуктов в программе.
Примеры сценариев использования:
- Выбор необходимой реализации FileSystemAcmeService или DataBaseAcmeSerivce или NetworkAcmeService во времени выполнения;
- Написание модульных тестов становится намного легче;
- Элементы графического пользовательского интерфейса для различных операционных систем.
Следствия
- Внедрение зависимости в Java скрывает зависимости класса, приводящие к ошибкам времени выполнения, которые могли-бы быть обнаружены во времени компиляции;
- Данный паттерн проектирования отлично справляется с задачей создания уже определённых объектов, но добавление новых может быть трудоёмким;
- Код становится сложнее, чем ему следует из-за появления большого количества новых интерфейсов и классов, которые появляются вместе с этим паттерном проектирования.
Руководства
Известные применения
- javax.xml.parsers.DocumentBuilderFactory
- javax.xml.transform.TransformerFactory
- javax.xml.xpath.XPathFactory
