Введение
Проектируя систему, мы занимаемся моделированием, а значит решаем инженерную задачу. Мы строим гипотезу о том, каковы отношения между сущностями в этой системе.
Однако бизнес-требования не вечны, они могут (и будут) меняться. Хорошо спроектированная система способна пережить эти изменения, отразить их в себе и продолжить функционировать.
Основная причина, по которой вносить изменения бывает трудно или дорого — когда небольшое изменение в одной части системы вызывает лавину изменений в других частях. Грубо и утрировано: если в программе для изменения цвета кнопки надо поправить 15 модулей, такая система спроектирована плохо.
Принцип открытости-закрытости
Принцип открытости-закрытости (Open-Closed Principle, OCP) помогает исключить такую проблему. Согласно ему модули должны быть открыты для расширения, но закрыты для изменения.
Простыми словами — модули надо проектировать так, чтобы их нельзя было менять, а новая функциональность должна появляться лишь с помощью создания новых сущностей и композиции их со старыми.
Основная цель принципа — помочь разработать проект, устойчивый к изменениям, срок жизни которых превышает срок существования первой версии проекта.
Модули, которые удовлетворяют OCP:
- открыты для расширения — их функциональность может быть дополнена с помощью других модулей, если изменятся требования;
- закрыты для изменения — расширение функциональности модуля не должно приводить к изменениям в модулях, которые его используют.
Конечно, всегда есть изменения, которые невозможно внести, не изменив код какого-то модуля — никакая система не может быть закрыта на 100%. Поэтому при проектировании важен стратегический подход. Необходимо определить, от каких именно изменений и какие именно модули вы хотите закрыть. Это решение следует принимать опираясь на опыт, а также знания предметной области и пользователей системы.
Нарушение принципа открытости-закрытости приводит к ситуациям, когда изменение в одном модуле вынуждает менять другие, связанные с ним. Это в свою очередь нарушает принцип единой ответственности, SRP, потому что весь код, который меняется по какой-то одной причине, должен быть собран в одном модуле. (Разные модули — разные причины для изменения.)
На примере
На схеме ниже объект Client
непосредственно связан с объектом Server
. Если нам вдруг понадобится, чтобы Client
мог работать с разными объектами Server
, нам придётся поменять его код.
Чтобы решить эту проблему, необходимо связывать объекты не напрямую, а через абстракции. Если все объекты Server
реализуют интерфейс Abstract Server
, то нам уже не придётся менять код объекта Client
для замены одного объекта Server
на другой.
Коротко
Принцип открытости-закрытости:
- заставляет проектировать модули так, чтобы они делали только одну вещь и делали её хорошо;
- побуждает связывать сущности через абстракции (а не реализацию) там, где могут поменяться бизнес-требования;
- обращает внимание проектировщиков на места стыка и взаимодействие сущностей;
- позволяет сократить количество кода, который необходимо менять при изменении бизнес-требований;
- делает внесение изменений безопасным и относительно дешёвым.