fedorthinks

Экспресс-курс · No. 04

Большинство из них ты уже не раз изобретал руками. Паттерн — это всего лишь имя: чтобы команда могла одним словом сказать то, на что ушёл бы абзац, и узнать приём раньше, чем сделает его криво.

20 паттернов · схема у каждого · шпаргалка в конце

§ 01

Паттерн проектирования — это названное, переиспользуемое решение проблемы, которая всплывает в коде снова и снова. Это словарь, а не библиотека — его не устанавливают, его узнают.

Design Pattern — имя для приёма, который ты и так делаешь

Футболисты не изобретают «стенку» по ходу матча. У приёма есть имя, и назвать его быстрее, чем описывать весь манёвр.

Паттерн проектирования (design pattern) — это приём, который толковые разработчики и так изобретают руками. Имя превращает абзац объяснений в одно слово, общее для всей команды. Когда кто-то говорит «оберни это в Decorator», все сразу видят форму — и в этом весь смысл паттерна: общий словарь для приёмов, которые ты сделал бы в любом случае.

ConceptDesign Pattern· паттерн проектирования
Что это
Названное решение проблемы, которая повторяется в коде снова и снова.
Чем не является
Не библиотека: его не устанавливают, а узнают и пишут сами.
Масштаб

Живёт внутри кода одного модуля — в отличие от архитектурных паттернов, которые решают планировку всей системы.

Зачем
Общий язык: одно слово вместо абзаца объяснений.

Gang of Four — паттерны находят, а не изобретают

Никто не изобретал арку. Строители раз за разом обнаруживали, что это самый прочный способ перекрыть пролёт, — и ей дали имя.

«Банда четырёх» (Gang of Four, GoF) — четыре автора книги «Design Patterns». Они не придумали 23 хитрых трюка; они каталогизировали решения, которые снова и снова всплывали в хорошем коде. Поэтому паттерн — не цель, в которую надо попасть, а форма, к которой ты приходишь сам, когда задача того требует. Если ты втискиваешь паттерн силой — скорее всего, у тебя ещё нет его проблемы.

Карта: три семьи, три вопроса

На кухне есть инструменты для трёх задач: приготовить еду, выложить на тарелку и подать. Паттерны делятся так же.

Порождающие (creational) паттерны — про то, как объекты рождаются: кто их строит и как. Структурные (structural) — про то, как объекты складываются в большие формы. Поведенческие (behavioural) — про то, как объекты общаются и делят ответственность. Почти любой классический паттерн — это один из этих трёх ответов.

Creationalhow objects are bornFactoryAbstract FactoryBuilderSingletonPrototypeStructuralhow objects fit togetherAdapterDecoratorFacadeProxyCompositeBehaviouralhow objects talk and share workStrategyObserverCommandChain of ResponsibilityStateIteratorTemplate MethodMediatorVisitorMemento
Три семьи и 20 паттернов курса — по ним идут главы 2–5.

Indirection — паттерн, который не нужен, это просто сложность

Швейцарский нож в кармане — удобно. Привинтить все двадцать инструментов к входной двери — нет.

Любой паттерн добавляет прослойку (indirection) — слой между тобой и вещью. Это оправдано, когда прослойка покупает гибкость, которой ты реально воспользуешься, и чистая трата — когда нет. Главная ошибка не в незнании паттернов, а в том, чтобы тянуться за паттерном раньше, чем появилась проблема.

Как читать каждый паттерн

Паспорт: одинаковые поля у всех, поэтому их легко сравнивать.

У каждого паттерна дальше одно и то же: английское название (это термин — запомни его), образ из жизни, схема и карточка с полями: проблема (что болит), идея (как решает), пример, цена (что ты платишь), когда брать и связи с другими паттернами. В конце курса — шпаргалка со всеми именами на одной странице.

Паттерн — это сокращение для разговора. Используй его, чтобы тебя поняли, а не чтобы выглядеть умно.

§ 02

Creational patterns — как объекты появляются на свет: без россыпи new повсюду и без приваривания кода к конкретным классам.

Factory — проси вещь по имени, а не по рецепту

Кофемашина: ты жмёшь «латте» и получаешь его. Ты не мелешь, не взбиваешь и не наливаешь сам — и не обязан знать, как это устроено.

Фабрика (Factory) выдаёт тебе объект, минуя прямой вызов конструктора, так что вызывающий код никогда не зависит от конкретного класса. Он знает только интерфейс. Это покупает свободу менять то, что строится, не трогая того, кто попросил.

Clientasks by namecreate("stripe")Factorypicks the classStripeProviderPayPalProviderFakeProviderthe caller knows only the PaymentProvider interface — never the concrete class
Клиент просит по имени; фабрика выбирает класс; клиент видит только интерфейс PaymentProvider.
PatternFactory· Factory Method, фабрика
Проблема
new StripeProvider() разбросан по коду — код приварен к классу.
Идея
Просить объект у фабрики; она решает, какой класс построить.
Пример

createPaymentProvider("stripe") возвращает PaymentProvider. Перейти на PayPal — поменять фабрику, а не каждое место вызова.

Цена
Лишняя прослойка. Фабрика, которая строит ровно одну вещь, — запах.
Когда брать
Реализаций несколько, или они будут меняться.
Связи
Abstract Factory — та же идея для целого семейства объектов.

Abstract Factory — целое семейство в едином стиле

Покупаешь мебельный гарнитур — «викторианский» или «модерн» — где каждый предмет приходит в одном стиле, без разношёрстных стульев.

Когда объекты должны идти согласованными семьями (families), абстрактная фабрика (Abstract Factory) строит весь набор сразу, так что части подходят друг к другу. Мощно — но это тяжёлая машинерия, оправданная, только когда семьи реальны.

macOS factoryButtonmacMenumacDialogmacWindows factoryButtonwinMenuwinDialogwinpick ONE familyone factory = one matching set; never a mac Button next to a Windows Menu
Одна фабрика — один согласованный набор: кнопка, меню и диалог в одном стиле.
PatternAbstract Factory· абстрактная фабрика
Проблема
Объекты должны сочетаться между собой, а их легко перемешать.
Идея
Одна фабрика выдаёт весь набор в одном стиле.
Пример

UI-тулкит: согласованные кнопки, меню и диалоги под macOS или под Windows из одной фабрики.

Цена
Тяжёлая машинерия: много интерфейсов и классов.
Когда брать
Только когда семейства настоящие, и их больше одного.
Связи

Строится из Factory. «Абстрактная фабрика на один продукт» — классика переусложнения.

Builder — собирай сложный объект шаг за шагом

Заказываешь бургер под себя — булка, котлета, без лука, двойной сыр — по одному выбору за раз, потом «готово».

Когда у объекта много необязательных частей, строитель (Builder) позволяет задавать их по кусочку, а не через монструозный конструктор на двенадцать аргументов. В конце вызываешь build() и получаешь готовый объект. Он меняет один сбивающий с толку конструктор на читаемую цепочку.

set one optional part at a timeselect(name)where(age>18)limit(10)build()Queryready, valid objectnew Query(a, b, c, … 12 args)which argument is which?instead ofa readable chain replaces a monstrous constructor
Цепочка шагов → build() → готовый объект. Вместо конструктора, где не понять, какой аргумент какой.
PatternBuilder· строитель
Проблема
Конструктор на двенадцать аргументов, половина из них необязательна.
Идея
Задавать части по одной, в конце — build().
Пример

QueryBuilder().select("name").where("age > 18").limit(10).build().

Цена
Ещё один класс рядом с основным.
Когда брать
У объекта много необязательных частей.
Связи

Factory решает, что построить; Builder — как собрать сложное.

Singleton — ровно один, общий для всех

У страны один президент. Все, кто говорит «президент», имеют в виду одного и того же человека — их не двое.

Одиночка (Singleton) гарантирует единственный экземпляр с одной глобальной точкой доступа. Но обращайся осторожно: singleton — это глобальное состояние в красивом пальто, а глобальное состояние мешает тестировать и рассуждать.

BillingAuthReportsAPIgetInstance()Configthe one instanceeveryone reaches the same global — handy, but hard to test
Все модули дотягиваются до одного и того же экземпляра — удобно, но это глобальное состояние.
PatternSingleton· одиночка
Проблема
Нужен ровно один общий экземпляр.
Идея
Класс сам гарантирует один экземпляр и одну точку доступа.
Пример
Конфигурация или логгер, из которых читают все.
Цена
Глобальное состояние: тесты и рассуждения становятся сложнее.
Когда брать

Редко. Часто лучше один экземпляр, который ты передаёшь явно, а не достаёшь через глобал.

Связи
Порождающий, как Factory, но про «сколько», а не про «какой».

Prototype — копируй заполненное, а не строй с нуля

Сделать копию заполненного бланка быстрее, чем заполнять пустой с нуля.

Когда создавать объект с нуля дорого или муторно, прототип (Prototype) предлагает клонировать (clone) существующий и подправить. Удобно, когда объекты дороги в постройке и в основном похожи.

Enemyconfigured: hp, skin, AIclone() + tweakcopy #1tweak: colourcopy #2tweak: speedcopy #3as ischeaper thanbuilding from zerocopy a filled-in object instead of rebuilding its setup
Настроенный объект клонируют и слегка правят — вместо того чтобы собирать заново.
PatternPrototype· прототип
Проблема
Собирать объект с нуля долго и муторно.
Идея
Взять готовый объект, скопировать, подправить отличия.
Пример

Дублировать настроенного игрового противника или стилизованный узел документа.

Цена
Копировать надо аккуратно: что копируется, а что остаётся общим.
Когда брать
Объекты дороги в постройке и в основном похожи.
Связи
Альтернатива Factory: не «построй», а «скопируй».

Спрячь new. Чем меньше мест знает, как строится объект, тем свободнее ты его меняешь.

§ 03

Structural patterns — как объекты складываются в большие структуры: стыкуя несовместимые части и добавляя возможности без переписывания.

Adapter — переходник между двумя интерфейсами

Зарядка ноутбука за границей — устройство в порядке, розетка в порядке; нужен только маленький переходник между ними.

Адаптер (Adapter) оборачивает один интерфейс так, чтобы он выглядел как другой. Две вещи, не созданные для совместной работы, начинают сотрудничать. Так ты не даёшь чужому API протечь во весь твой код.

Your codeexpects your interfacepay()Adaptertranslates callscharge()Vendor SDKits own shapeyour code never sees the vendor's shape
Твой код зовёт pay(); адаптер переводит это в charge() вендора.
PatternAdapter· адаптер, wrapper
Проблема
Чужой интерфейс не совпадает с твоим и расползается по коду.
Идея
Обёртка, которая переводит вызовы из одного интерфейса в другой.
Пример

Сторонний платёжный SDK обёрнут так, что удовлетворяет твоему PaymentProvider.

Цена
Ещё один слой, который надо обновлять, когда меняется API вендора.
Когда брать
Подключаешь сторонний или старый код.
Связи

Оборачивает объект, как Decorator и Proxy, но меняет интерфейс.

Decorator — добавляй начинку, не меняя пиццу

Простая пицца, потом сыр, потом грибы — каждый слой оборачивает предыдущий и что-то добавляет, и это всё ещё пицца.

Декоратор (Decorator) оборачивает объект и добавляет поведение — без правки оригинала и без наследования. Интерфейс остаётся тем же, поэтому слои можно ставить друг на друга. Так ты добавляешь возможности в сочетаниях, которые никто не обязан был предусмотреть.

Encrypt decoratorCompress decoratorFileStreamthe originalwrite()each layer wraps the last, adds one thing, keeps the same interface
Поток обёрнут в сжатие, а сжатие — в шифрование. Снаружи это всё ещё поток.
PatternDecorator· декоратор, обёртка
Проблема

Нужны добавки в любых сочетаниях; подклассов на каждое не напасёшься.

Идея
Обернуть объект, добавить одно поведение, сохранить интерфейс.
Пример

Поток данных → сжатие → шифрование; каждый слой можно поставить или снять.

Цена
Много мелких обёрток; стек слоёв труднее отлаживать.
Когда брать
Возможности надо комбинировать в рантайме.
Связи

По форме как Proxy, но цель — добавить, а не контролировать. Чистый пример composition over inheritance.

Facade — одна простая стойка перед бардаком за кулисами

Консьерж в отеле — ты говоришь «нужно такси и столик на ужин», а он разруливает клубок звонков за стойкой.

Фасад (Facade) даёт простую, единую точку входа в сложную подсистему (subsystem) и прячет её движущиеся части. Он сжимает большую поверхность до одной двери, которая нужна большинству вызывающих.

ClientVideoConverterconvert(file, mp4)one doorfor most callerssubsystemCodecsBuffersAudio streams
Один вызов convert() — а за ним кодеки, буферы и аудиопотоки.
PatternFacade· фасад
Проблема
Чтобы сделать простую вещь, надо знать десяток классов подсистемы.
Идея
Одна простая дверь, которая дирижирует подсистемой.
Пример

VideoConverter.convert(file, "mp4") втихую управляет кодеками, буферами и аудиопотоками.

Цена

Сложность не исчезает — она спрятана. Тонкий контроль всё равно идёт мимо фасада.

Когда брать
Большинству вызывающих нужен один-два простых сценария.
Связи

Adapter меняет интерфейс одной вещи; Facade упрощает целую подсистему.

Proxy — подставное лицо, контролирующее доступ

Ассистент знаменитости — ты не дотягиваешься до звезды напрямую; ассистент фильтрует, планирует, а иногда отвечает за неё.

Заместитель (Proxy) встаёт перед объектом и контролирует доступ к нему: для ленивой загрузки (lazy loading), прав, кэширования или общения с чем-то удалённым. Тот же интерфейс — лишние ворота перед ним.

ClientProxysame interfaceonly if neededRealImageheavy, loaded latelazy load · access checkcache · remote call
Клиент говорит с прокси как с настоящим объектом; прокси решает, когда и пускать ли дальше.
PatternProxy· заместитель
Проблема

Объект дорого грузить, к нему нужны права, или он вообще на другой машине.

Идея
Заместитель с тем же интерфейсом стоит перед настоящим объектом.
Пример

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

Цена
Лишний шаг в каждом вызове; задержка спрятана от вызывающего.
Когда брать
Ленивая загрузка, права, кэш, удалённый объект.
Связи
Форма как у Decorator, цель другая — контроль доступа.

Composite — дерево и лист на один манер

Папка хранит файлы и другие папки. «Посчитать размер» работает одинаково, спрашиваешь ли ты один файл или всё дерево.

Компоновщик (Composite) позволяет обращаться с одиночным объектом (лист, leaf) и группой объектов (ветка, composite) одинаково — у них один интерфейс. Так рекурсивные структуры превращаются в то, с чем работаешь без особых случаев на каждом уровне.

Folderfile.txtFolderphoto.jpga.mdb.mdgetSize() and render() work the same on one leaf and on a whole tree
Папка и файл отвечают на одни и те же вызовы — дерево обходится без особых случаев.
PatternComposite· компоновщик, дерево
Проблема
Работа с деревом требует особого случая на каждом уровне.
Идея
Лист и ветка делят один интерфейс; ветка просто зовёт детей.
Пример

Интерфейс, где и кнопка, и панель-полная-кнопок отвечают на render() и getSize().

Цена

Общий интерфейс бывает слишком общим: не все операции имеют смысл у листа.

Когда брать
Данные по природе — дерево: папки, UI, документы.
Связи

Обходят его через Iterator; новые операции добавляют через Visitor.

Композиция вместо наследования: защёлкивай поведение в рантайме, а не замораживай его в дереве классов.

§ 04

Первая половина behavioural patterns: как объекты делят работу и говорят друг с другом — чтобы ответственность была разрезана чисто и нужный код запускался в нужный момент.

Strategy — подмени алгоритм, оставь вызывающего

Приложение карт: та же поездка, но ты выбираешь «быстрее», «короче» или «без платных дорог», и маршрут строится иначе. Ты выбираешь стратегию; приложение просто следует ей.

Стратегия (Strategy) делает алгоритм взаимозаменяемым в рантайме: вызывающий остаётся прежним, а способ меняется. В современных языках это часто просто передача функции, а не целого класса — и в этом суть: паттерн — это идея, а не церемония.

Checkoutuses a strategyPricingStrategyinterface / functionRegularMemberHolidaySaleswap the algorithm at runtime; the caller never changes
Оформление заказа зовёт PricingStrategy; какая именно цена — решается в рантайме.
PatternStrategy· стратегия
Проблема
Растущая лестница if по вариантам одного алгоритма.
Идея
Алгоритм — подменяемый объект (или функция) за одним интерфейсом.
Пример

Оформление заказа принимает PricingStrategy: обычная, для участника или праздничная распродажа.

Цена
Strategy на один алгоритм, который никогда не меняется, — лишняя.
Когда брать
Вариантов два и больше, и выбор делается в рантайме.
Связи
По форме — как State. В современном коде часто просто функция.

Observer — подпишись, и узнаешь, когда изменится

Рассылка — ты подписываешься один раз, и каждый новый выпуск приходит к тебе. Издатель не звонит каждому читателю; он просто шлёт по списку.

Наблюдатель (Observer): объекты-подписчики (subscribers) подписываются на субъект (subject) и получают уведомление, когда тот меняется. Субъект при этом не знает, кто они. Это становой хребет событийного и реактивного кода.

notify()Subjectspreadsheet cellChartPivot tableSummarythe subject keeps a list of subscribers — it doesn't know who they are
Ячейка изменилась → notify() → все зависящие графики и таблицы обновились сами.
PatternObserver· наблюдатель, pub/sub
Проблема
Объект должен оповещать многих, не зная их поимённо.
Идея
Список подписчиков + notify() при каждом изменении.
Пример

Ячейка таблицы обновляет все зависящие графики; UI-фреймворки реагируют на состояние; pub/sub повсюду.

Цена
Цепочки уведомлений трудно отследить; забытая подписка живёт вечно.
Когда брать
На одно изменение реагируют несколько независимых частей.
Связи

Основа событийного кода — того же Event-driven, только внутри одной программы.

Command — оберни запрос в объект

Заказной чек в ресторане — запрос «стол 5, два кофе» становится бумажкой, которую можно поставить в очередь, переставить, залогировать и даже порвать.

Команда (Command) превращает действие в самостоятельный объект. Теперь его можно поставить в очередь, залогировать, отложить или отменить (undo). Так действие становится тем, что можно сохранить и проиграть заново.

Editoruser actioncommand historyTypeTextdo() · undo()Bolddo() · undo()Deletedo() · undo()executeDocumentreceiveran action becomes an object: queue it, log it, undo it, replay it
Каждая правка — объект с do() и undo(); история команд даёт undo/redo и макросы.
PatternCommand· команда
Проблема
Действие нужно отложить, повторить, залогировать или отменить.
Идея
Упаковать действие в объект со всем, что нужно для выполнения.
Пример

Каждая правка в редакторе — команда: отсюда undo/redo и макросы. Очередь задач — команды, ждущие своей очереди.

Цена
Объект на каждое действие; отмену надо продумать для каждой команды.
Когда брать
Undo/redo, очереди, макросы, журнал действий.
Связи
Для undo часто идёт в паре с Memento.

Chain of Responsibility — передавай дальше, пока кто-то не разберётся

Уровни поддержки — первая линия пробует, эскалирует специалисту, затем менеджеру, пока кто-то реально не решит вопрос.

Цепочка обязанностей (Chain of Responsibility): запрос идёт по цепочке обработчиков (handlers); каждый либо разбирается с ним, либо передаёт дальше. Так отправитель отвязан от того, кто в итоге сделает работу.

middleware chainRequestAuthLoggingRate limitRoutehandles it401, stopeach link handles its part — or passes the request on
Веб-middleware: auth → логирование → rate limit → маршрут. Любое звено может остановить запрос.
PatternChain of Responsibility· цепочка обязанностей, middleware
Проблема
Отправитель не должен знать, кто именно обработает запрос.
Идея
Цепочка обработчиков: каждый делает свою часть или передаёт дальше.
Пример

Веб-middleware: аутентификация, потом логирование, потом rate-limit, потом сам маршрут.

Цена

Порядок звеньев важен; запрос может пройти всю цепь и никем не обработаться.

Когда брать
Несколько независимых проверок или шагов перед основной работой.
Связи
В вебе ты почти всегда встречаешь его под именем middleware.

Хорошие поведенческие паттерны дают двум кускам сотрудничать, не вшивая один в другой намертво.

§ 05

Вторая половина behavioural patterns: как поведение объекта меняется с его состоянием, как ты идёшь по коллекциям и как фиксируешь скелет, оставляя один шаг открытым.

State — поведение, которое меняется с режимом

Светофор — зелёный, жёлтый, красный — где каждый цвет решает, что происходит сейчас и какой цвет будет следующим. Светофор тот же, поведение разное в каждом состоянии.

Состояние (State) позволяет объекту менять поведение по мере смены внутреннего состояния. Клубок if и switch заменяется ясными состояниями (states) и переходами (transitions) — это маленький конечный автомат (state machine). Так каша из флагов превращается в аккуратную машинку.

pendingpaidshippedcancelledpay()ship()cancel()cancel()each state knows only its own rules and what it can become
Заказ: pending → paid → shipped; отменить можно только из pending и paid.
PatternState· состояние, state machine
Проблема
Поведение зависит от режима, и код зарастает флагами и switch.
Идея
Каждое состояние — отдельный объект со своими правилами и переходами.
Пример

Заказ ведёт себя по-разному как pending, paid, shipped или cancelled; каждое состояние знает только свои правила и то, чем может стать.

Цена
Много маленьких классов; на два состояния хватит обычного if.
Когда брать
Состояний много, а переходы между ними важны.
Связи

По форме как Strategy, но объект переключает состояние сам, по ходу жизни.

Iterator — иди по коллекции, не заглядывая внутрь

Кнопки «дальше» и «назад» на пульте — ты переключаешь каналы, не зная, как они хранятся.

Итератор (Iterator) даёт единый способ пройти коллекцию, не раскрывая, как она устроена — массив, дерево или связанный список. Он настолько полезен, что язык обычно встраивает его внутрь — for..of, генераторы, __iter__. Это самый ясный пример того, как паттерн дорастает до фичи языка.

ArrayTreeLinked listIteratornext() · hasNext()for..of loopsame code for allwalk any collection without seeing how it is stored
Массив, дерево и список отдают один и тот же next() — цикл не знает, что внутри.
PatternIterator· итератор
Проблема
Код обхода зависит от того, как устроена коллекция.
Идея
Единый способ брать «следующий элемент», что бы ни было внутри.
Пример

for (item of collection) работает одинаково над списком и над своей структурой.

Цена
Почти нулевая — обычно он уже встроен в язык.
Когда брать
Всегда, но через встроенные средства, а не руками.
Связи
Обходит деревья из Composite.

Template Method — фиксированный рецепт с одним пропуском

Рецепт, одинаковый каждый раз, — кроме «добавь специю на свой вкус» на четвёртом шаге. Скелет зафиксирован; один шаг — твой.

Шаблонный метод (Template Method) задаёт скелет (skeleton) операции в базовом классе и позволяет переопределять отдельные шаги, не меняя общей формы. Полезно, когда много потоков делят форму, но различаются в одном-двух местах.

fixed skeleton in the base classopen()read()parse()you fill this insave()CSV parseJSON parsesubclasses supplyonly this one step
Базовый импортёр открывает, читает, разбирает и сохраняет; подклассы дают только шаг разбора.
PatternTemplate Method· шаблонный метод
Проблема
Несколько потоков одной формы копируют друг друга почти целиком.
Идея
Скелет — в базовом классе; изменчивые шаги — в подклассах.
Пример

Импортёр открывает, читает, разбирает (это заполняешь ты) и сохраняет; импортёры CSV и JSON подставляют только шаг разбора.

Цена
Держится на наследовании; скелет трудно поменять потом.
Когда брать
Форма общая, различия — в одном-двух местах.
Связи
Strategy решает похожую задачу через композицию, а не наследование.

Mediator, Visitor, Memento — остальное на одном дыхании

В ящике с инструментами есть те, за которыми тянешься раз в месяц, а не каждый день, — но знать об их существовании всё равно стоит.

Ещё горстка иногда оправдывает своё место. Mediator — диспетчерская, чтобы объекты говорили через один хаб, а не клубок прямых связей. Visitor — добавить новые операции к структуре, не правя её классы. Memento — захватить состояние объекта, чтобы восстановить его позже. Знай имена; тянись за ними, только когда появилась именно эта проблема.

MediatorVisitorMementoABCDHubone hub, not a tangleCircleSquareTextExportVisitornew operationclasses stay untouchedEditorSnapshotsaved statesaverestoreundo · save points
Mediator: всё через хаб. Visitor: новая операция обходит классы снаружи. Memento: снимок и восстановление.
PatternMediator· посредник
Что это
Один хаб, через который объекты общаются вместо прямых связей.
Образ
Диспетчерская вышка: самолёты говорят с ней, а не друг с другом.
Когда брать
Прямых связей между объектами стало слишком много.
PatternVisitor· посетитель
Что это
Новая операция над структурой, вынесенная в отдельный объект.
Пример
Экспорт всех фигур документа — без правки классов самих фигур.
Когда брать
Структура стабильна, а операций над ней становится всё больше.
PatternMemento· снимок
Что это
Сохранённое состояние объекта, к которому можно вернуться.
Пример
Точки сохранения в игре, снимки для отмены в редакторе.
Связи
Вместе с Command даёт undo.

Когда паттерн становится ключевым словом языка — перестань писать паттерн и используй ключевое слово.

§ 06

Каталог — это десятилетия добытой потом мудрости, и одновременно продукт своего времени. Пользуйся им как сеньор, а не как чек-листом.

Language features — многие паттерны теперь просто фичи языка

Раньше дорогу объясняли по ориентирам; теперь телефон сам ведёт. Навык не исчез — он впитался в инструмент.

Iterator — это for..of. Strategy — часто переданная функция. Decorator и Observer встроены в современные фреймворки. Когда язык даёт тебе форму бесплатно, писать полный паттерн руками — просто церемония (boilerplate). Идея по-прежнему важна; шаблонный код — обычно нет.

the patternwhat the language gives you nowIteratorfor..of · generators · __iter__Strategya function passed inDecoratorbuilt into frameworksObserverreactive state in UI frameworks
Слева паттерн из каталога, справа — что язык или фреймворк уже дают вместо него.

Pattern-itis — реальная болезнь

Повар, вываливающий в одно блюдо всю полку специй, получает не более богатый вкус, а несъедобное месиво.

Паттерн-ит (pattern-itis) — классический запах переусложнения (over-engineering): тянуться за паттернами, чтобы выглядеть изощрённо. И сейчас хуже — AI-ассистент с радостью сгенерит «многоуровневую Abstract Factory» там, где должно быть три строки. Более простой код почти всегда — более сеньорский выбор.

Anti-patternPattern-itis· паттерн-ит, over-engineering
Что это
Паттерны ради паттернов, а не ради снятой боли.
Симптомы

Абстрактная фабрика на один продукт, Strategy на один алгоритм, который никогда не меняется, пять слоёв обёрток вокруг одного if.

Почему сейчас хуже

AI-ассистенты генерируют такие конструкции охотно и быстро.

Лекарство
Сначала самый простой код; паттерн — только когда появилась боль.

Паттерны — это словарь, а не закон

Знание множества слов не делает хорошего писателя. Хорошего делает знание, какое слово — и когда промолчать.

Настоящая ценность каталога — общий язык: «давай поставим тут Facade» мгновенно доходит до любого, кто знает термин, и на команде это стоит немало. Но цель всегда — самый простой код, решающий задачу. Паттерн — это средство; назвать его — никогда не достижение.

Выучи все паттерны, чтобы их узнавать. Используй так мало, как только можешь себе позволить.

§ 07

Паттерны — это ответы. Мастерство — подобрать их к реальному вопросу и понять, когда правильный ответ — никакого паттерна.

Пусть паттерн вырастает из боли

Ты не берёшь гаечный ключ, пока не нашёл болт. Сначала видишь проблему — потом тянешься за инструментом, который к ней подходит.

Хорошее применение паттерна — это обычно рефакторинг (refactoring), а не стартовый чертёж: ты пишешь простое, чувствуешь конкретную боль — эта лестница if всё растёт, этот класс слишком много знает о том, как он построен, — и тогда паттерн, который её снимает, очевиден. Втискивать паттерны заранее — это как строить соборы, которых никто не просил.

Называй, только когда имя помогает

Ты не говоришь «я выполнил стенку» в обычной беседе. Но на поле, с партнёрами, это слово экономит целое предложение.

Выигрыш от паттерна — это общение и гибкость. Если имя делает код яснее для следующего человека, произнеси его вслух — в имени класса, в комментарии, в обсуждении дизайна. Если обёртка не покупает ничего — побеждает самый простой код, с паттерном или без.

Паттерны связаны между собой

Слова в языке не живут поодиночке: у них есть синонимы, пары и родственники.

Adapter, Decorator и Proxy — одна форма: обёртка вокруг объекта, разные цели. Strategy и State похожи по форме. Command и Memento вместе дают undo. Abstract Factory строится из Factory. Composite обходят Iterator. Observer — основа событийного кода.

all wrap one objectAdapterchanges the interfaceDecoratoradds behaviourProxycontrols accessStrategyStatesame shapeCommandMementoundoAbstract FactoryFactoryusesCompositeIteratorwalk the treeObserverEvent-driven codebackbone ofChain of Responsibility= web middleware
Главные связи между паттернами курса.
Прежде чем тянуться за паттерном
  • Какую повторяющуюся проблему он решает? Скажи одним предложением.
  • Есть ли у меня эта проблема сейчас, или я гадаю про будущее?
  • Справилась бы обычная функция или объект? Сначала попробуй так.
  • Не даёт ли мне язык это уже бесплатно?
  • Сделает ли имя код яснее для следующего человека — или просто наряднее?
  • Я рефакторю к нему или втискиваю заранее?
Признаки, что ты переусложнил
  • У тебя фабрика, которая строит ровно одну вещь.
  • Strategy с единственной стратегией, которая никогда не меняется.
  • Интерфейсов и обёрток больше, чем реального поведения.
  • Ты добавил паттерн, чтобы выглядеть сеньором, а не чтобы снять боль.
  • Новому читателю нужна UML-диаграмма, чтобы проследить три строки логики.
Признаки, что ты выбрал хорошо
  • Паттерн убрал растущую лестницу if/switch.
  • Ты можешь подменить реализацию, не трогая вызывающих.
  • Коллега узнал форму по имени сразу.
  • Обёртка окупается — ты реально пользуешься гибкостью.
  • С паттерном код проще, чем без него, а не просто хитрее.

Шпаргалка: все 20 паттернов и главные идеи

Основы

Design Patternпаттерн проектирования

Названное решение повторяющейся проблемы в коде. Словарь, а не библиотека.

Gang of FourGoF, «банда четырёх»

Авторы книги «Design Patterns», которые собрали каталог из 23 паттернов.

Indirectionпрослойка

Слой между тобой и вещью. Цена любого паттерна.

Pattern-itisпереусложнение

Паттерны ради вида, а не ради боли. Простой код почти всегда лучше.

Порождающие (creational)

FactoryFactory Method

Просишь объект по имени; фабрика выбирает класс.

Abstract Factoryабстрактная фабрика

Фабрика целого согласованного семейства объектов.

Builderстроитель

Сложный объект по шагам, в конце build().

Singletonодиночка

Ровно один экземпляр на всех. Осторожно: глобальное состояние.

Prototypeпрототип

Клонируй готовый объект и подправь.

Структурные (structural)

Adapterадаптер

Переходник: один интерфейс выглядит как другой.

Decoratorдекоратор

Обёртка добавляет поведение, интерфейс тот же.

Facadeфасад

Одна простая дверь в сложную подсистему.

Proxyзаместитель

Стоит перед объектом и контролирует доступ.

Compositeкомпоновщик

Лист и дерево — один интерфейс.

Поведенческие: общение (behavioural)

Strategyстратегия

Подменяемый алгоритм; часто просто функция.

Observerнаблюдатель

Подписчики узнают об изменениях сами.

Commandкоманда

Действие как объект: очередь, лог, undo.

Chain of Responsibilityцепочка обязанностей

Обработчики по цепочке; в вебе — middleware.

Поведенческие: состояние и шаги

Stateсостояние

Каждое состояние — свои правила и переходы.

Iteratorитератор

Обход коллекции, не заглядывая внутрь. Обычно for..of.

Template Methodшаблонный метод

Скелет зафиксирован, один шаг — твой.

Mediatorпосредник

Все говорят через один хаб.

Visitorпосетитель

Новая операция без правки классов.

Mementoснимок

Сохранить состояние и вернуться к нему.

Лучшее применение паттерна — то, которого читатель даже не замечает, потому что код стал казаться очевидным.

Конец экспресс-курса · 7 глав · 20 паттернов

Дальше — первоисточник: «Design Patterns» «банды четырёх» и её более добрые современные спутники — «Head First Design Patterns», refactoring.guru, «Рефакторинг» Мартина Фаулера. Но читай их так, как учат шахматные дебюты: не чтобы зазубрить ходы, а чтобы узнавать позицию, когда она появится на твоей доске.