Зачем паттернам имена
Что это такое
Паттерн — это описание повторяющейся задачи, сил, которые делают её трудной, и формы решения вместе с его последствиями. Имя служит ручкой ко всему этому. Паттерны появились из наблюдений за реальным кодом: одни и те же структуры возвращались снова и снова, поэтому каталог получился описательным, а не предписывающим — он фиксирует то, что работает, вместе с ценой.
Почему это важно
Без этой рамки ломаются две вещи: первая — обращение с паттернами как с ингредиентами, когда в программу с одной реализацией добавляют фабрику «потому что фабрики хороши» и платят за косвенность, которая ничего не решает. Вторая — плохое переизобретение известного решения: автор не знал, что у задачи есть имя и документированный набор подводных камней. Обе ошибки стоят времени. Если понимать, что паттерн есть компромисс, вопрос перестаёт звучать как «стоит ли применять паттерн»; вместо него появляется «хочу ли я платить эту цену за эту гибкость».
Ключевые идеи
Паттерн — общее имя для компромисса
Имя сжимает абзац обсуждения дизайна в одно слово, и если бы паттерн не давал этого сжатия, его не стоило бы вносить в каталог: сам код уже выражает структуру. Ценность проявляется в разговоре — «сделай это Наблюдателем» или «это Декоратор, который оборачивает транспорт» сразу понятно на ревью и несёт известный набор последствий.
"здесь нужна Стратегия" ->
интерфейс в точке вариации, выбор за вызывающим
Каждый паттерн стоит косвенности
Паттерн почти всегда добавляет тип, интерфейс или уровень делегирования. Платить эту цену стоит тогда, когда обслуживаемая вариация реальна и, как ожидается, будет расти; когда она гипотетическая — не стоит. Спросите себя, сколько реализаций есть сегодня и насколько вероятна третья. Одна реализация и никаких планов на другую — вызов функции, надевший костюм.
напрямую: вызывающий -> реализация
Стратегия: вызывающий -> интерфейс -> одна реализация
Паттерны, идиомы и антипаттерны
Идиома — соглашение конкретного языка, стандартный способ перебора или написания типа ошибки, и называть её паттерном не стоит: это просто то, как язык используется. Антипаттерн — форма, которая выглядит как решение, но надёжно приносит беду; пример — класс, знающий обо всех остальных классах. Назвать его полезно именно затем, чтобы его узнавали и обходили.
Сначала должна существовать задача
Самое частое злоупотребление — применить паттерн к задаче, которая, как вы ожидаете, появится. Проектируйте под ту вариацию, что есть сегодня, и рефакторьте к паттерну, когда появится второй случай. Рефакторинг в паттерн обычно дёшев; гораздо дороже удалить паттерн, оказавшийся ненужным, ведь тогда придётся убирать типы, от которых теперь зависит другой код.
два случая сегодня -> напишите второй случай, посмотрите, что у них общего
три случая -> выделите форму, назовите её и отрефакторьте
Паттерны описывают взаимодействия, а не классы
Важно, как части работают вместе: кто что создаёт, кто кого вызывает и какая часть о какой знает. Две реализации одного паттерна могут выглядеть в коде очень по-разному — одна через наследование, другая через замыкания, — и всё же оставаться одним паттерном, потому что взаимодействие одно и то же. Читайте паттерн как схему ответственностей, а не как файл для копирования.
Практика
Назовите то, что у вас уже есть
Выберите в своём коде место, где одна функция принимает флаг или режим и ведёт себя по-разному в зависимости от него. Решите, реальна ли вариация, и если реальна — назовите паттерн, который описывает это разделение.
Готово, когда: вы можете сформулировать паттерн или причину, по которой паттерн не нужен, по одному предложению на каждый.
Посчитайте цену гипотетического паттерна
Возьмите в своём коде место с единственной реализацией за интерфейсом. Запишите, что пришлось бы изменить в интерфейсе при добавлении второй реализации и во что обошёлся бы вместо этого прямой вызов.
Готово, когда: у вас есть письменный ответ на оба направления, и вы можете сказать, какую цену предпочли бы платить уже сейчас.
Частые ошибки
- Применение паттерна к задаче, которой пока нет — косвенность без вариации, которая её оправдывала бы; исправить: ждите второго или третьего случая и тогда рефакторьте в паттерн.
- Вера в то, что паттерны — чек-лист качества — код обзаводится церемониями, не приобретая гибкости; исправить: судите каждый паттерн по конкретной цене, которую он берёт.
- Буквальное копирование диаграммы классов паттерна — код отращивает типы, существующие лишь ради соответствия диаграмме; исправить: приспосабливайте взаимодействие к языку, сохраняя ответственности.
- Путаница идиомы с паттерном — обычное употребление языка называют решением дизайна; исправить: оставьте имена паттернов для форм с настоящим компромиссом.
- Использование имени, чтобы прекратить обсуждение — «это Одиночка» не аргумент; исправить: назовите паттерн и затем сформулируйте последствие, которое вас беспокоит.
Что читать дальше
- Принципы SOLID (01-cs-fundamentals/09-solid-principles) — свойства дизайна, ради которых существует большинство паттернов, поэтому прочтите первым.
- Паттерны, которые оправдывают себя (01-cs-fundamentals/18-design-patterns/02-the-patterns-that-earn-their-keep) — конкретный набор для изучения, когда рамка ясна.
- Переиспользование кода (01-cs-fundamentals/16-code-reusability) — тот же вопрос о выделении общей формы, заданный до того, как паттерн назван.