Паттерн проектирования — это описание задачи, которая регулярно возникает в коде, и проверенного способа её решить. Ценность здесь не в самой конструкции: почти любой паттерн можно написать самостоятельно, не зная названия. Ценность в общем словаре. Когда в команде говорят «здесь нужен адаптер», всем понятно, что имеется в виду, и обсуждение занимает минуту вместо получаса. Ниже разбираем основные группы паттернов, их связь с принципами хорошего кода и границы применимости — потому что вреда от неуместного шаблона не меньше, чем пользы от уместного.
Три группы паттернов
Классическое деление выделяет три категории по типу решаемой задачи.
- Порождающие отвечают за создание объектов: как получить нужный экземпляр, не привязываясь к конкретному классу.
- Структурные описывают, как объединять объекты и классы в более крупные структуры и как согласовывать несовместимые интерфейсы.
- Поведенческие занимаются распределением обязанностей и способом взаимодействия объектов между собой.
Деление условное: некоторые решения попадают сразу в две категории, а архитектурные схемы вроде разделения на модель, представление и контроллер состоят из нескольких паттернов сразу. Тем не менее для разговора о коде такое разделение удобно.
Порождающие: контроль над созданием объектов
Проблема, которую они решают, звучит так: код, которому нужен объект, не должен знать, как именно этот объект собирается. Иначе каждое изменение в сборке разъезжается по всему проекту.
- Фабричный метод задаёт интерфейс создания, а решение о конкретном классе оставляет наследникам. Удобно, когда вариантов несколько и они добавляются со временем.
- Абстрактная фабрика создаёт семейства связанных объектов: например, набор компонентов под одну платформу, чтобы они не перемешались с компонентами другой.
- Строитель собирает сложный объект по шагам. Применяется, когда у объекта много необязательных параметров и конструктор с десятком аргументов становится нечитаемым.
- Одиночка гарантирует единственный экземпляр. Самый спорный паттерн: он вводит глобальное состояние и усложняет тестирование, поэтому мы применяем его редко и осознанно.
- Прототип создаёт объект копированием готового, когда сборка с нуля дорога.
Структурные: собрать целое из частей
Эта группа полезнее всего в проектах, где приходится работать с чужим кодом и внешними сервисами.
- Адаптер приводит один интерфейс к другому. Типичный случай — подключение внешнего сервиса, у которого свой формат данных, к приложению, где принят собственный.
- Фасад прячет сложную подсистему за простым интерфейсом. Когда для отправки заказа нужно дёрнуть пять сервисов, фасад оставляет наружу один метод.
- Декоратор добавляет поведение объекту, не меняя его класс: логирование, кеширование, повтор при ошибке.
- Заместитель подставляет объект-посредника: отложенная загрузка, контроль доступа, кеш ответов.
- Компоновщик позволяет работать с деревом объектов так же, как с одним объектом. Хорошо ложится на категории каталога и вложенные меню.
Поведенческие: кто что делает
- Наблюдатель описывает связь «один ко многим»: при изменении объекта заинтересованные получают уведомление. На этом устроены подписки на события в большинстве современных фреймворков.
- Стратегия выносит взаимозаменяемые алгоритмы в отдельные классы. Расчёт доставки разными способами — учебный, но точный пример.
- Команда превращает вызов в объект, который можно поставить в очередь, повторить или отменить.
- Состояние заменяет разрастающиеся условные конструкции набором классов, по одному на состояние заказа или заявки.
- Шаблонный метод фиксирует последовательность шагов, оставляя часть шагов на переопределение.
Какая задача каким паттерном решается
| Ситуация в коде | Подходящее решение | Что это даёт |
|---|---|---|
| Внешний сервис отдаёт данные в чужом формате | Адаптер | Остальной код не знает про формат поставщика |
| Ветвление по типу оплаты разрослось на сотню строк | Стратегия | Новый способ добавляется отдельным классом |
| Конструктор с десятью необязательными параметрами | Строитель | Сборка объекта читается сверху вниз |
| После действия нужно уведомить несколько подсистем | Наблюдатель | Подписчики добавляются без правки источника |
| Логика заказа зависит от его статуса | Состояние | Переходы описаны явно, ветвлений меньше |
| Нужно добавить кеш, не трогая класс | Декоратор или заместитель | Поведение подключается снаружи |
| Для одной операции дёргается пять сервисов | Фасад | Вызывающий код упрощается |
Связь с принципами SOLID
Паттерны и принципы описывают одно и то же с разных сторон: принципы говорят, к чему стремиться, паттерны — как этого достичь в конкретной ситуации.
- Единственная ответственность. Класс делает одно дело. Стратегия и команда прямо помогают вынести лишнее из разросшегося класса.
- Открытость и закрытость. Поведение расширяется без правки существующего кода. Это цель декоратора, стратегии и фабричного метода.
- Подстановка Лисков. Наследник должен работать везде, где работает родитель. Нарушения обычно всплывают там, где наследованием заменили композицию.
- Разделение интерфейсов. Лучше несколько узких интерфейсов, чем один общий. Фасад и адаптер помогают держать наружу ровно то, что нужно.
- Инверсия зависимостей. Код зависит от абстракций. Внедрение зависимостей — практическая реализация этой идеи, а фабрики отвечают за сборку.
Когда паттерн вредит
Мы регулярно принимаем на поддержку проекты, где паттерны применены заранее, «на вырост». Итог одинаковый: чтобы понять путь одного запроса, приходится открыть восемь файлов, а изменить простую вещь дороже, чем в прямолинейном коде.
- Абстракция ради единственной реализации, которая так и не получила вторую.
- Интерфейс на каждый класс, включая те, что никогда не подменяются.
- Слои-обёртки, которые только передают вызов дальше.
- Паттерн, выбранный по названию задачи, а не по её сути.
Практическое правило: паттерн вводится, когда задача уже проявилась дважды. Первый случай решается прямо, второй показывает, что общего, и тогда обобщение оправдано.
Как это выглядит в поддержке чужого кода
В унаследованных проектах паттерны чаще не вводят с нуля, а используют как способ безопасно вмешаться. Нужно подменить работу с внешним сервисом — появляется адаптер. Нужно добавить кеширование, не трогая рабочую логику, — появляется заместитель. Нужно разгрузить класс, в котором две тысячи строк, — часть поведения выносится стратегиями, по одной за подход.
Такой путь безопаснее переписывания: каждое изменение локально, его можно откатить, и проект остаётся рабочим на каждом шаге.
Что стоит запомнить
- Паттерн — это словарь для общения команды, а не обязательный элемент проекта.
- Три группы: создание объектов, сборка структуры, распределение поведения.
- Принципы SOLID отвечают на вопрос «зачем», паттерны — на вопрос «как».
- Обобщение вводится по факту повторения, а не заранее.
- В унаследованном коде паттерны удобны как способ вмешаться точечно.
Если проекту нужны разработчики, которые разберутся в существующем коде и не станут переписывать его целиком, обсудим формат работы: выделенная команда разработки под задачи вашего продукта.











