Files
Vladimir 376cd21527 translation: Translate Abstract Factory to Russian / Fix #2286 partially (#2533)
* Translate Abstract Factory to Russian

* Fix a typo
2023-08-20 10:35:57 +03:00
..

title, category, language, tag
title category language tag
Abstract Factory Creational ru
Gang of Four

Альтернативные названия

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 скрывает зависимости класса, приводящие к ошибкам времени выполнения, которые могли-бы быть обнаружены во времени компиляции;
  • Данный паттерн проектирования отлично справляется с задачей создания уже определённых объектов, но добавление новых может быть трудоёмким;
  • Код становится сложнее, чем ему следует из-за появления большого количества новых интерфейсов и классов, которые появляются вместе с этим паттерном проектирования.

Руководства

Известные применения

Родственные паттерны проектирования

Благодарности