Эта статья изначально была написана на польском языке и опубликована в моем блоге.

Factory — довольно популярный и простой в использовании шаблон проектирования. В литературе выделяют 4 типа фабрик:

  • Фабрика
  • Заводской метод
  • Статическая фабрика
  • Абстрактная фабрика

Фабрика принадлежит к семейству творческих шаблонов проектирования.

В этой статье я расскажу и проиллюстрирую все упомянутые типы Factory. Основная идея Фабрики, как и настоящей фабрики, заключается в создании объектов. Использование Фабрики позволяет скрыть детали реализации создания объектов и отделить их от слоя бизнес-логики. В статье все варианты шаблона проектирования Factory будут объясняться с помощью коротких фрагментов кода на TypeScript. К счастью, для понимания подготовленных фрагментов требуется знакомство только с основами объектно-ориентированных концепций. Хорошо, так что давайте пошутим!

Фабрика

Лучший способ показать практический вариант использования и преимущества использования шаблона проектирования Factory — показать его на примере. Поддерживаемая вами система включает в себя систему продаж и должна создавать счета для клиентов. Поскольку ваши клиенты из разных стран, счета-фактуры должны соответствовать их потребностям. Примеры корректировок:

  • формат даты, например. дд/мм/гггг, мм/дд/гггг;
  • направление текста — слева направо или справа налево;
  • валюта.

Потенциально это создает большое количество комбинаций и возможностей, и обработка их внутри бизнес-логики может привести к тому, что бизнес-код станет нечитаемым и сложным в обслуживании. Это также сделало бы бизнес-код подверженным ошибкам и трудным для расширения. Шаг вперед в дизайне решения — создать простую фабрику, где все типы счетов можно было бы обернуть в отдельный закрытый метод. Созданная фабрика будет предоставлять только один общедоступный метод с объектом param в качестве ожидаемого ввода. На основании значений параметров будет выставлен счет, готовый к печати. Код бизнес-логики ничего не знает о деталях создания счетов для конкретных стран. Фабрика рассматривается как черный ящик, который создает счет на основе предоставленных параметров. Наиболее тривиальная реализация Фабрики может выглядеть следующим образом (я намеренно пропустил части кода, которые не требуются для иллюстрации паттерна Фабрика):

Когда реализация Factory готова, все, что вам нужно сделать, это создать экземпляр InvoiceFactory и вызвать метод createInvoice. Результатом вызова упомянутого метода является готовый к печати счет, который может быть возвращен клиенту с помощью фрагмента кода, отвечающего за обработку бизнес-логики. Процесс формирования счета в коде бизнес-логики может выглядеть следующим образом:

Текущая реализация позволяет создавать счета для клиентов из Польши, США и Саудовской Аравии. Чтобы добавить поддержку других стран, созданную фабрику можно очень легко расширить. Все, что вам нужно сделать, это создать новый случай в общедоступном методе, новый частный метод и новый специальный класс для конкретной страны. Обратите внимание, что я решил создать отдельный класс для каждой страны. В противном случае Фабрика закончилась бы одним раздутым классом, которого вы наверняка хотели бы избежать.

Заводской метод

Решение, предложенное в предыдущем разделе, имеет одну существенную проблему. За исключением частей кода, характерных для конкретной страны, есть часть кода, общая для всех счетов-фактур, например формат файла или настройки шрифта (размер, цвет, семейство и т. д.).

Для решения этой проблемы можно использовать шаблон проектирования Factory Method. Благодаря этому можно извлечь часть кода, общую для всех классов инвойсов. Предыдущая реализация, преобразованная в реализацию фабричного метода, может выглядеть следующим образом:

Общая часть процесса генерации счетов была заключена в метод _generateInvoice в абстрактном классе Invoice. Конкретные реализации для конкретных стран реализованы в конкретных классах в методе защищенного формата. Приведенный выше код сочетает в себе шаблоны проектирования Factory Method и Factory. Он показывает, что шаблоны проектирования можно использовать вместе и смешивать для получения желаемых результатов! Комбинированные шаблоны привели к тому, что решение было понятный, легко расширяемый и свободный от дублирования кода.

Простота решения приводит к тому, что знакомство нового инженера-программиста с этой частью кода займет меньше времени и повысит его производительность. Инженеру не обязательно знать низкоуровневую логику, связанную с процессом генерации счетов. Все, что им нужно сделать, это реализовать очень простой класс с конечным числом опций и присоединить его к фабрике.

Статическая фабрика

Когда завод был готов, возникла еще одна проблема. В системе было создано несколько экземпляров InvoiceFactory. Несколько клиентов получили счета с одним и тем же идентификатором. Чтобы устранить эту проблему, мы могли бы ввести Static Factory. В этом случае мы можем реализовать простое решение в памяти, которое будет хранить идентификаторы уже сгенерированных счетов, чтобы предотвратить описанную ситуацию. В случае дублирования будет выброшена ошибка. Пример реализации Static Factory может выглядеть следующим образом:

Статические фабрики также полезны в случае различных вспомогательных и служебных классов. В этом случае довольно часто нет смысла создавать экземпляр фабричного класса, и проще полагаться на статические методы. Вызов HelperClass.doX( params )намного проще, чем создание нового экземпляра HelperClass.

Абстрактная фабрика

Описанная система продаж успешно запущена в производство. Через некоторое время вашей команде был доставлен новый набор требований. Теперь не только счета-фактуры, но и внутренние отчеты о продажах должны генерироваться системой. Все типы документов должны создаваться через единый интерфейс. Более того, система должна быть готова к расширению новыми типами документов в будущем. Решением этой проблемы является создание Абстрактной фабрики. Короче говоря, абстрактная фабрика использует в своей реализации другие фабрики и в то же время является фабрикой. Пример абстрактной фабрики показан кодом ниже:

Готовое решение отвечает всем перечисленным бизнес-требованиям. Здесь я должен подчеркнуть, что все фабрики должны возвращать документ, соответствующий интерфейсу IDocument. Абстрактные фабрики — идеальное решение, если ваша система производит различные объекты с общим интерфейсом. В нашем случае все созданные объекты являются документами.

Краткое содержание

Надеюсь, после прочтения статьи идея шаблона проектирования Фабрика вам ясна. Я призываю вас попробовать описанные фабрики в ваших проектах. Также буду признателен, если вы похлопаете и оставите комментарий! Ниже вы найдете дополнительные материалы и источники.

Исходники и дополнительные материалы