Шаблоны проектирования простым языком. Часть первая. Порождающие шаблоны
Первая статья из цикла, посвящённого шаблонам, или паттернам, проектирования. На понятных примерах объясняем суть порождающих шаблонов.

Рассказывает Камран Ахмед
Шаблоны проектирования — это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.
Википедия описывает их следующим образом:
Будьте осторожны
- шаблоны проектирования не являются решением всех ваших проблем;
- не пытайтесь использовать их в обязательном порядке — это может привести к негативным последствиям. Шаблоны — это подходы к решению проблем, а не решения для поиска проблем;
- если их правильно использовать в нужных местах, то они могут стать спасением, а иначе могут привести к ужасному беспорядку.
Также заметьте, что примеры ниже написаны на PHP 7. Но это не должно вас останавливать, ведь принципы остаются такими же.
Типы шаблонов
Шаблоны бывают следующих трех видов:
- Порождающие.
- Структурные.
- Поведенческие.
Если говорить простыми словами, то это шаблоны, которые предназначены для создания экземпляра объекта или группы связанных объектов.
Википедия гласит:
Существуют следующие порождающие шаблоны:
- простая фабрика (Simple Factory);
- фабричный метод (Factory Method);
- абстрактная фабрика (Abstract Factory);
- строитель (Builder);
- прототип (Prototype);
- одиночка (Singleton).
Простая фабрика (Simple Factory)
Википедия гласит:
Пример из жизни: Представьте, что вам надо построить дом, и вам нужны двери. Было бы глупо каждый раз, когда вам нужны двери, надевать вашу столярную форму и начинать делать дверь. Вместо этого вы делаете её на фабрике.
Простыми словами: Простая фабрика генерирует экземпляр для клиента, не раскрывая никакой логики.
Перейдем к коду. У нас есть интерфейс Door
и его реализация:
Затем у нас есть наша DoorFactory
, которая делает дверь и возвращает её:
И затем мы можем использовать всё это:
Когда использовать: Когда создание объекта — это не просто несколько присвоений, а какая-то логика, тогда имеет смысл создать отдельную фабрику вместо повторения одного и того же кода повсюду.
Пример на Java.
Фабричный метод (Fabric Method)
Википедия гласит:
Пример из жизни: Рассмотрим пример с менеджером по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии он должен распределить этапы собеседования между разными людьми.
Простыми словами: Менеджер предоставляет способ делегирования логики создания экземпляра дочерним классам.
Перейдём к коду. Рассмотрим приведенный выше пример про HR-менеджера. Изначально у нас есть интерфейс Interviewer
и несколько реализаций для него:
Теперь создадим нашего HiringManager
:
И теперь любой дочерний класс может расширять его и предоставлять необходимого интервьюера:
Пример использования:
Когда использовать: Полезен, когда есть некоторая общая обработка в классе, но необходимый подкласс динамически определяется во время выполнения. Иными словами, когда клиент не знает, какой именно подкласс ему может понадобиться.
Примеры на Java и Python.
Абстрактная фабрика (Abstract Factory)
Википедия гласит:
Пример из жизни: Расширим наш пример про двери из простой фабрики. В зависимости от ваших нужд вам понадобится деревянная дверь из одного магазина, железная дверь — из другого или пластиковая — из третьего. Кроме того, вам понадобится соответствующий специалист: столяр для деревянной двери, сварщик для железной двери и так далее. Как вы можете заметить, тут есть зависимость между дверьми.
Простыми словами: Фабрика фабрик. Фабрика, которая группирует индивидуальные, но связанные/зависимые фабрики без указания их конкретных классов.
Обратимся к коду. Используем пример про двери. Сначала у нас есть интерфейс Door
и несколько его реализаций:
Затем у нас есть несколько DoorFittingExpert
для каждого типа дверей:
Теперь у нас есть DoorFactory
, которая позволит нам создать семейство связанных объектов. То есть фабрика деревянных дверей предоставит нам деревянную дверь и эксперта по деревянным дверям. Аналогично для железных дверей:
Пример использования:
Как вы можете заметить, фабрика деревянных дверей инкапсулирует столяра и деревянную дверь, а фабрика железных дверей инкапсулирует железную дверь и сварщика. Это позволило нам убедиться, что для каждой двери мы получим нужного нам эксперта.
Когда использовать: Когда есть взаимосвязанные зависимости с не очень простой логикой создания.
Примеры на Java и Python.
Строитель (Builder)
Википедия гласит:
Пример из жизни: Представьте, что вы пришли в McDonalds и заказали конкретный продукт, например, БигМак, и вам готовят его без лишних вопросов. Это пример простой фабрики. Но есть случаи, когда логика создания может включать в себя больше шагов. Например, вы хотите индивидуальный сэндвич в Subway: у вас есть несколько вариантов того, как он будет сделан. Какой хлеб вы хотите? Какие соусы использовать? Какой сыр? В таких случаях на помощь приходит шаблон «Строитель».
Простыми словами: Шаблон позволяет вам создавать различные виды объекта, избегая засорения конструктора. Он полезен, когда может быть несколько видов объекта или когда необходимо множество шагов, связанных с его созданием.
Давайте я покажу на примере, что такое «Телескопический конструктор». Когда-то мы все видели конструктор вроде такого:
Как вы можете заметить, количество параметров конструктора может резко увеличиться, и станет сложно понимать расположение параметров. Кроме того, этот список параметров будет продолжать расти, если вы захотите добавить новые варианты. Это и есть «Телескопический конструктор».
Перейдем к примеру в коде. Адекватной альтернативой будет использование шаблона «Строитель». Сначала у нас есть Burger
, который мы хотим создать:
Затем мы берём «Строителя»:
Пример использования:
Когда использовать: Когда может быть несколько видов объекта и надо избежать «телескопического конструктора». Главное отличие от «фабрики» — это то, что она используется, когда создание занимает один шаг, а «строитель» применяется при множестве шагов.
Примеры на Java и Python.
Прототип (Prototype)
Википедия гласит:
Пример из жизни: Помните Долли? Овечка, которая была клонирована. Не будем углубляться, главное — это то, что здесь все вращается вокруг клонирования.
Простыми словами: Прототип создает объект, основанный на существующем объекте при помощи клонирования.
То есть он позволяет вам создавать копию существующего объекта и модернизировать его согласно вашим нуждам, вместо того, чтобы создавать объект заново.
Обратимся к коду. В PHP это может быть легко реализовано с использованием clone
:
Затем он может быть клонирован следующим образом:
Также вы можете использовать волшебный метод __clone
для изменения клонирующего поведения.
Когда использовать: Когда необходим объект, похожий на существующий объект, либо когда создание будет дороже клонирования.
Примеры на Java и Python.
Одиночка (Singleton)
Википедия гласит:
Пример из жизни: В стране одновременно может быть только один президент. Один и тот же президент должен действовать, когда того требуют обстоятельства. Президент здесь является одиночкой.
Простыми словами: Обеспечивает тот факт, что создаваемый объект является единственным объектом своего класса.
Вообще шаблон одиночка признан антипаттерном, необходимо избегать его чрезмерного использования. Он необязательно плох и может иметь полезные применения, но использовать его надо с осторожностью, потому что он вводит глобальное состояние в ваше приложение и его изменение в одном месте может повлиять на другие части приложения, что вызовет трудности при отладке. Другой минус — это то, что он делает ваш код связанным.
Подробнее о подводных камнях шаблона одиночка читайте в нашей статье.
Перейдем к коду. Чтобы создать одиночку, сделайте конструктор приватным, отключите клонирование и расширение и создайте статическую переменную для хранения экземпляра:
Пример использования:
Пример на Java.