Skip to Content
ПродвинутыйПринцип открытой закрытостиГлавное

Главное

Объяснение

Программные сущности должны быть открыты для расширения, но закрыты для изменения.

По факту это значит, что сущность может иметь «ребёнка» (наследника или реализацию интерфейса), но изменяться сама не будет.
Новая функциональность добавляется через написание нового кода, а не через правку существующего.

На практике OCP достигается с помощью абстракций (интерфейсов, абстрактных классов) и полиморфизма — вместо цепочек if/else мы используем стратегии, цепочки обязанностей, декораторы и другие паттерны, позволяющие «подключать» новые модули, не трогая старые.

Примеры

Что за примеры?

  • MetadataHandler – в AuthGuard используется цепочка обработчиков для проверки метаданных (@Public(), @SkipAuthGuard()). Новый тип метаданных (например, @Roles()) добавляется созданием нового отдельного Guard (RolesGuard), который также использует MetadataHandler со своим набором обработчиков. AuthGuard остаётся без изменений. Это пример OCP через паттерн «Цепочка обязанностей» и разделение аутентификации и авторизации.

  • Стратегии авторизации – StrategiesService динамически загружает OAuth2-стратегии (Google, GitHub). Чтобы добавить новый провайдер, достаточно дописать конфигурацию и класс стратегии – основной код остаётся нетронутым. Реализация паттерна «Стратегия».

  • CrudService и наследование – базовый CrudService предоставляет общие CRUD-методы. Для каждой модели (User, Post, Notification) создаётся сервис-наследник, который переопределяет или расширяет поведение. Базовый класс закрыт для изменений, но открыт для расширения через наследование. Показаны компромиссы: сложность типизации и когнитивная нагрузка.

  • IEventPublisher: нарушение и исправление – интерфейс с методами для каждого события (PublishOrderCreated, PublishProductUpdated) нарушает OCP — новое событие требует изменения интерфейса. Показаны три способа исправления: обобщённый метод, атрибуты и адаптация с обратной совместимостью.

  • Notification Emitters – иерархия эмиттеров уведомлений. Базовый класс BaseNotificationsEmitter содержит общую логику, конкретные эмиттеры (CommentNotificationsEmitter, ReactionNotificationsEmitter, FriendshipNotificationsEmitter) расширяют его. Новый тип уведомлений — новый класс-наследник.

  • BitBuilder – статические фабричные методы (fromConfig, fromData) предоставляют разные сценарии использования одного движка генерации битов. Новый сценарий — новый статический метод.

  • BitField и BitFieldView – разделение представления (BitFieldView) и операций (BitField). Базовый класс закрыт для изменений, новые операции добавляются через наследование. Иммутабельность упрощает тестирование и предотвращает побочные эффекты.

  • Configurable Utilities – три утилиты (SlugPipe, Routes, Enumeration) показывают OCP через конфигурацию, дженерики и фабрики. Новые типы добавляются без изменения существующего кода.

Каждый пример сопровождается анализом «что хорошо», «что плохо» и финальным улучшенным кодом.

Чек-лист: не нарушаете ли вы OCP?

Доступно по ссылке

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

Last updated on