Переосмысление GRASP (разделения ответственности - General Responsibility Assignment Software Patterns), SOLID (единственной ответственности, открытый-закрытый, подстановки Лисков, разделения интерфейсов, инверсии зависимостей), GoF (Банды четырех - Gang of Four) для фронтенда (браузерное программирование) & бекенда (node.js, другие рантаймы) разработки на JavaScript и TypeScript
- 🧩 Патерны
- 📢 GoF патерны для Node.js и JavaScript (фрагмент семинара)
- 🏭 Порождающие шаблоны
- Абстрактная фабрика (Abstract factory) — создает связанные объекты, принадлежащие одному из семейств, без указания их конкретных классов, например, визуальные компоненты под разные платформы.
- Строитель (Builder) — пошаговая сборка сложного объекта с возможностью конфигурации, часто с помощью чеининга, например, Query Builder или Form Generator.
- Фабрика (Factory) — функция или метод для создания объектов различными способами: сборки по частям из литералов объектов и отдельных методов, через примеси, setPrototypeOf.
- Фабричный метод (Factory method) — выбирает нужную абстракцию для создания экземпляра, в JavaScript это можно сделать через if, switch или выбор конструктора из коллекции.
- Прототип (Prototype) — клонирование объекта из заранее подготовленного экземпляра, для экономии ресурсов на создание (не путать с прототипным наследованием, оно ближе к Flyweight).
- Flyweight — экономия выделения памяти для группы объектов через проброс доступа к общему (разделяемому) состоянию, у конкретного инстанса.
- Одиночка (Singleton) — глобальный доступ к единственному экземпляру, часто считаться анти-паттерном, проще всего реализовать через кэш системы модульности ESM/CJS.
- Object Pool — повторное использование заранее созданных объектов для экономии ресурсов при частом создании и уничтожении.
- 🤝 Структурные шаблоны
- Адаптер (Adapter) — конвертор, преобразует несовместимый интерфейс в совместимый, позволяя использовать сторонний компонент без изменения его кода, можно даже преобразовать контракт функции в объект или наоборот.
- Обертка (Wrapper) — обертка над функцией с пробросом вызова (делегирование) с добавлением поведения, частный случай паттерна Adapter.
- Boxing — упаковка примитивов в объектные типы для добавления методов или унификации интерфейсов, например, можно сузить String до AddressString.
- Decorator — динамически расширяет поведение без наследования, обычно через композицию и декларативный синтаксис, по сути добавляет метаданные.
- Прокси (Proxy) — контролирует доступ к объекту, перехватывая вызовы, чтение и запись, может применяться для ленивой инициализации, кэширования и безопасности, может реализовываться как в GoF или встроенным в JavaScript Proxy.
- Мост (Bridge) — разделение двух и более иерархий абстракций за счет композиции или агрегации, позволяя им изменяться независимо.
- Компоновщик (Composite) — реализует общий интерфейс, который позволяет единообразно работать с отдельными объектами деревьями объектов, например, DOM или файловая система.
- Фасад (Facade) — упрощает доступ к сложной системе, предоставляя потребителю (использующему коду) единый и понятный интерфейс, для защиты и сокрытия сложности.
- Легковес (Flyweight) — экономия выделения памяти для группы объектов через проброс доступа к общему (разделяемому) состоянию, у конкретного инстанса.
- Context — обмен состоянием и зависимостями между различными компонентами (абстракциями, модулями, слоями), не имеющими общей среды, без жесткого их зацепления.
- ⚡ Поведенческие шаблоны
- Цепочка обязанностей (Chain of responsibility) — передача управления по цепочке обработчиков для выбора одного ответственного, все в цепочке читают, но менять может только один.
- Middleware — цепочка обработчиков, как CoR, но каждый может изменять состояние и передавать управление дальше, что может привести к гонке, конфликтам, ошибкам.
- Команда (Command) — инкапсулирует действие (запрос исполнения) и его параметры в объект, чтоб передавать исполнителю, ставить в очередь, отменять, повторять и т.д.
- Интерпретатор (Interpreter) — реализация языка (DSL - domain specific language) или разбор выражений в AST (абстрактное синтаксическое дерево) с возможностью интерпретации.
- Итератор (Iterator) — обход коллекции или потока поэлементно, без доступа ко всем данным, можно сделать как в GoF, но в JavaScript есть встроенные Iterator и AsyncIterator.
- Посредник (Mediator) — оптимизация взаимодействия между N компонентами, что потребовало бы N * (N - 1) / 2 связей, а централизация взаимодействия снижает зацепление до N.
- Снимок (Memento) — сохранение и восстановление истории снимков состояния объекта, без прямого доступа к самому состоянию.
- Наблюдатель (Observable) — уведомление подписчиков об изменении состояния объекта.
- EventEmitter для Node.js: Observable + listener
- EventTarget для Web API: EventTarget + Event (CustomEvent) + listener
- Signal
- Состояние (State) — реализация конечного автомата (Automaton или FSM), где методы - это переходы, а состояние добавляется через композицию и меняется на переходах.
- Стратегия (Strategy) — выбор взаимозаменяемого поведения в рантайме, через коллекцию реализаций: функций, объектов, классов.
- Шаблонный метод (Template method) — фиксирует шаги алгоритма, позволяя подклассам переопределять отдельные шаги, и использовать шаги предка как дефолтное поведение.
- Посетитель (Visitor) — позволяет добавлять операции к объектам без изменения их классов, разделяя структуру и поведение, на несколько абстракций.
- Открытый конструктор (Revealing Constructor) - изменение поведения без наследования, внедрение поведения в конструктор в виде функции или объекта, содержащего поведение и его описание.
- Actor — Инкапсулирует состояние и поведение, взаимодействуя асинхронно через передачу и последовательную обработку сообщений в очереди. Обеспечивает потокобезопасность и асинхронную безопасность при параллельном выполнении путём изоляции состояния актора.
- Reactor (event-loop) — Обрабатывает параллельные события синхронно, помещая их в очередь и направляя зарегистрированным обработчикам. Реализует событийно-ориентированную асинхронную обработку поверх синхронного цикла событий. Часто применяется в системах с интенсивным I/O, упрощая управление конкурентными событиями.
- Proactor — Цикл событий, в котором операции начинаются пользовательским кодом, но завершаются внешним агентом (например, I/O подсистемой), который запускает обработчик завершения по завершении операции (возвращая данные в callback).
- Service Locator - центральный реестр сервисов, который позволяет нам регистрировать и извлекать абстракции между модулями (используется для внедрения зависимостей, в js может быть реализован через систему модулей ESM или CJS).
- 🗃️ Шаблоны доступа к данным
- Transaction Script — процедурный шаблон, в котором каждая бизнес-операция реализуется как функция (процедура или скрипт).
- Pattern SAGA — шаблон распределённой транзакции, в котором сложный бизнес-процесс разбивается на последовательность малых транзакций, каждая из которых имеет компенсирующее действие на случай сбоя. Позволяет избежать распределённых блокировок.
- Unit of Work — шаблон отслеживает изменения в бизнес-объектах и координирует сохранение как одну атомарную операцию в ORM или Repository, инкапсулируя всю работу в рамках транзакции.
- Table Module — шаблон, в котором вся доменная логика, связанная с таблицей базы данных, инкапсулируется в одном классе или модуле, при этом строки рассматриваются как простые данные.
- Value Object — неизменяемый, самопроверяющийся объект, представляющий концепт в домене без идентификатора, используется для выражения доменных ограничений и согласованности логики значений, сравнения по значению в типобезопасной, явно выраженной форме.
- Null Object — объект, реализующий стандартный интерфейс, но предоставляющий нейтральное поведение «do-nothing». Предназначен для избежания проверок на null, упрощения логики и обеспечения полиморфной безопасности. Является заменой действия "по умолчанию", устраняющей условные конструкции и проверки через гварды.
- Active Record — доменный объект, инкапсулирующий запись таблице базы данных и предоставляющий методы для непосредственного выполнения операций CRUD (создание, чтение, обновление, удаление) и специфичных запросов к себе.
- Data access object (DAO) — абстракция, определяющая интерфейс для сохранения и извлечения доменных объектов, изолируя доменную логику от конкретных реализаций хранилища.
- Data transfer object (DTO) — анемичный объект (только данные) без доменного поведения, предназначенный исключительно для передачи структурированных данных между слоями, модулями, подсистемами или архитектурными границами.
- Data Access Layer (DAL) — слой, абстрагирующий доступ к множеству DAO или сырых источников данных. Может быть реализован как шаблон Facade. Часто включает трансформации данных.
- Repository — доменно-центричная абстракция для доступа к данным, возвращающая доменные сущности, а не сырые данные или DTO.
- См. другие шаблоны: Template method, Actor, State, Memento
- 🧩 Шаблоны (патерны или принципы) GRASP
- Общий обзор GRASP
- GRASP Part 1: Information expert, Creator, Low coupling, High cohesion
- GRASP Part 2: Protected variations, Indirection, Pure fabrication, Polymorphism, Controller
- Информационный эксперт (Information expert)
- Ненаправленность (Indirection)
- Низкое зацепление (Low coupling)
- Высокая связность (High cohesion)
- Чистая выдумка (Pure fabrication)
- Примеры кода
- Information Expert - распределяйте обязанность по задачам на те абстракции, которые имеют для этого нужные данные. Связано: encapsulation, cohesion, coupling, information hiding, SOLID: SRP, SoC.
- Creator - Если одна абстракция пишет, читает, аггрегирует, использует, сильно зацеплена с доругой, то она и должна ее создавать и инициализировать. Связфно: Information Expert, GoF Creational patterns.
- Controller - содержит use-case сценарии для обработки внешних I/O запросов, от UI, API или шины собыий и делегирует выполнение другим абстракциям. Связано: GoF Command, Facade, Layers, Pure Fabrication.
- Low Coupling - каждая абстракция минимально зависит от деталей реализации других, содержит минимум "знаний" (обращений). Дает устойчивость, простоту тестирования и сопровождения. Связано: High Cohesion, Controller, Indirection, DIP, DI, IoC, Revealing constructor, Facade, Mediator, Observer, Strategy, State, Bridge, Adapter, Proxy.
- High Cohesion - все внутренние элементы абстракции тесно связаны общей целью и "знают" контракты друг друга, совместно решают одну конкретную задачу. Такие абстракции легко понимаются, тестируются и сопровождается. Связано: Low Coupling, Information Expert, Composite, Facade, Adapter. Высокая связанность внутри модуля и низкое зацепление между модулями дают устойчивость системы.
- Polymorphism - при помощи динамической диспетчеризации абстракции выбирают поведение и делегируют действия объектам с общим интерфейсом вместо явного ветвления по типу. Связано: GoF Strategy, Adapter, Creator, Command, State, Bridge, Template Method, Visitor, Factory Method, Proxy.
- Pure Fabrication - искуственные абстракции, которые не относятся к предметной области, а обслуживают структурные, архитектурные и технические нужды. Связано: SRP, ISP, GoF: Facade, Adapter, Observer, Command, Mediator, Repository, Service. Примеры: EventEmitter, Stream, Connection, Promise, Error.
- Indirection - посредник для реализации слабого зацепления между компонентами. Связано: GoF Mediator, Facade, Observer, Service Layer, API Gateway, Message Broker, Event Bus.
- Protected Variations - защищает абстракции от изменения с помощью вынесения взаимодействия в фиксированный интерфейс, только через который возможно взаимодействие между абстракциями. Связано: Interface, Contract programing, Generics, OCP, DIP, DI, IoC, GoF: Strategy, Bridge, Abstract Factory, Factory Method, Adapter, Proxy, Facade.
- 🧩 Шаблоны (патерны или принципы) SOLID
- 📢 Вводная лекция: SOLID for Node.js and Javascript
- SOLID вопросы на интервью
- Принцип единственной ответственности (Single responsibility principle)
- Принцип открытости/закрытости (Open/closed principle)
- Принцип подстановки Лисков (Liskov substitution principle)
- Принцип разделения интерфейса (Interface segregation principle)
- Принцип инверсии зависимостей (Dependency inversion principle)
- Single responsibility principle - у класса должна быть только одна причина для изменения. «Модуль должен отвечать за одного и только за одного актора.»
- Open-closed principle - абстракции (классы, типы и т.д.) должны быть открыты для расширения, но закрыты для модификации.
- Liskov substitution principle - функции, которые используют базовый тип, должны иметь возможность использовать подтипы базового типа не зная об этом.
- Interface segregation principle - много интерфейсов, специально предназначенных для клиентов, лучше, чем один интерфейс общего назначения.
- Dependency inversion principle - Зависимость на Абстракциях. Нет зависимости на конкретное.