Принципы SOLID.
2. Принцип «открыт/закрыт» (OCP)
«Программные сущности (классы, модули, функции и т. п.) должны быть открытыми для расширения, но закрытыми для модификации» (Мартин Р. Принципы, паттерны и практики гибкой разработки. — 2006).
Одна из причин «загнивания» дизайна кроется в страхе внесения изменений. Разработчики и менеджеры должны быть уверены в том, что изменения являются корректными и не приведут к появлению ошибок в других частях системы. Простые классы и модули, которые соответствуют принципу единственной обязанности, являются хорошей стартовой точкой, но этого не всегда достаточно.
По мере развития в системе появляются семейства типов с общим поведением и схожими интерфейсами. Возникают иерархии наследования, в базовых классах которых помещается общее поведение, которое наследники изменяют при необходимости. Это позволяет повторно использовать значительную часть логики, а также упрощает добавление типов с новым поведением. Полученные иерархии типов одновременно являются открытыми (просто добавлять новые типы) и закрытыми (интерфейсы базовых классов стабильны).
Смысл принципа OCP: дизайн системы должен быть простым и устойчивым к изменениям.
Т.е. когда требования изменятся, вы должны быть к этому готовы. Не нужно создавать дополнительные уровни абстракции без необходимости, но нужно ограничить каскад изменений и свести их количество к минимуму. Как этого добиться?
1. За счет абстракции и инкапсуляции. Выделение важных частей системы в открытой части класса позволяет сосредоточиться на важных аспектах поведения, не задумываясь о реализации, скрытой от клиентов в закрытой его части. При этом абстракция не требует наличия интерфейсов или абстрактных классов.
Сокрытие информации заключается не только в наличии закрытых полей, недоступных клиентам, но и подразумевает сокрытие реализации, т.е. позволяет думать о классе как о чёрном ящике, который предоставляет определённые в интерфейсе услуги лишь ему известным способом.
Таким образом в классе разделяют интерфейс и реализацию: в интерфейсе собрано всё, что касается взаимодействия другими объектами; реализация скрывает от других объектов детали, не имеющие отношения к процессу взаимодействия.
2. За счет наследования. Выделение интерфейсов и абстрактных классов позволяет думать о задаче ещё более абстрактно. Полиморфное поведение позволяет заменить один вариант реализации на другой во время исполнения, а также повторно использовать код.
Принцип единственного выбора
Всякий раз, когда система должна поддерживать множество альтернатив, их полный список должен быть известен только одному модулю системы. Фабрика отвечает принципу «открыт/закрыт», если список вариантов является её деталью реализации. Если же информация о конкретных типах иерархии начинает распространяться по коду приложения и в нем появляются проверки типов (
as или is), то это решение уже перестанет следовать принципу «открыт/закрыт». Тогда добавление нового типа потребует каскадных изменений в других модулях, что негативно отразится на стоимости изменения.Примеры нарушения OCP
1. Интерфейс класса является нестабильным. Постоянные изменения интерфейса класса, используемого во множестве мест, приводят к постоянным изменениям во многих частях системы.
2. «Размазывание» информации об иерархии типов. В коде постоянно используются понижающие приведения типов (downcasting), что «размазывает» информацию об иерархии типов по коду приложения. Это затрудняет добавление новых типов и усложняет понимание текущего решения.
Источник: Тепляков С. "Паттерны проектирования на платформе .NET." — СПб.: Питер, 2015. Глава 18.