Привет Re-Programmerz !
В продолжение нашей серии Обязанности разработчика как профессионала и, как было обещано в нашей предыдущей статье, Золотое правило выбора хорошей архитектуры , мы подробно рассмотрим пять принципов SOLID, на этот раз со второй буквой: O для принцип открытости-закрытости (OCP).
Определение
Этот принцип программирования впервые был введен Бертраном Мейером в 1988 году и гласил:
«Программные объекты (классы, модули, функции и т. д.) должны быть открыты для расширения, но закрыты для модификации».
Позже Роберт Мартин переформулирует его следующим образом:
«Вы должны иметь возможность расширять поведение системы, не изменяя ее».
Разные подходы
Суть OCP заключается в том, что изменение поведения модуля (открыт для расширения) не требует больших усилий, и это изменение не должно вызывать никаких изменений исходного кода (закрыто для модификация). После компиляции модуль не следует изменять и перекомпилировать. Вместо этого его следует оставить как есть и расширить, когда возникнет необходимость в изменении поведения.
Для достижения этой цели Бертран Мейер предложил использовать Наследование.
«Класс закрыт, так как он может компилироваться, храниться в библиотеке, базироваться и использоваться клиентскими классами. Но он также открыт, поскольку любой новый класс может использовать его как родительский, добавляя новые функции. Когда определен класс-потомок, нет необходимости изменять исходный или беспокоить его клиентов ».
Роберт С. Мартин в своих беседах и книгах предлагает использовать Полиморфизм (абстрактные классы или интерфейсы) , соблюдая при этом два других принципа: Принцип единой ответственности и Принцип инверсии зависимостей .
Как узнать, не соблюдается ли OCP в моем коде?
Теперь давайте разберемся, сначала на теоретическом примере, а затем мы приведем код в действие!
В ролевой игре для ПК движения и действия запускаются с клавиатуры.
- Чтобы привлечь больше игроков, игровая компания хочет, чтобы они также обрабатывались с помощью различных типов входов (геймпады, джойстики…). Имея в виду OCP при разработке игры (а также других принципов SOLID), каждый раз, когда появляется новый источник ввода, команда разработчиков просто добавляет реализацию для этого нового и добавляет ее в список параметров ввода.
- Позже, по какой-то причине, игровая компания решает, что геймпад Y больше не будет поддерживаться. Это не потребует от команды особых усилий: удалите реализацию геймпада Y, удалите геймпад Y из списка параметров ввода, работа будет выполнена. Гибкость кода делает удаление таким же «простым», как и добавление.
В обоих случаях все остальные основные игровые модули должны остаться без изменений, что избавит команду от перекомпиляции миллионов строк исходного кода.
… Давайте напишем код!
Приведенный выше фрагмент кода содержит InputController
класс, реализующий pause
функцию, которая принимает один параметр: input
, набранный как GameControlInput
.
Реализация функции pause
представляет собой некоторый switch
оператор типа параметра input
, чтобы вызвать правильный код действияpause
на правильном контроллере ввода.
Другими словами:
- Если параметр
input
является клавиатурой, вызываетсяKeyboard.selectEsc
. - Если параметр
input
- XboxGamePad, вызываетсяXboxGamePad.selectMenu
. - Если параметр
input
- это PlayStationGamePad, вызываетсяPlayStationGamePad.selectOption
.
Это не идеально, но работает, давайте продолжим ...
Появляется новое требование…!
Игровая панель Y появляется на рынке, и компания хочет оставаться в курсе последних событий, поддерживая эту новинку в своей игре.
Что происходит выше?
Мы создаем новый класс с именем YGamePad
того же типа, что и другие входные данные.
Теперь нам нужно изменить функцию pause
, чтобы использовать этот новый класс…
Чего ждать?!
Ясно, что здесь мы нарушаем OCP. Наш InputController
класс должен быть открыт для расширения, а не для модификации!
Но как мы можем это сделать? OCP в действии!
Сделаем небольшие изменения ...
GameControlInput
больше не класс, а интерфейс.- Функция
pause
внутри классаInputController
имеет параметр, тип которого соответствует интерфейсуGameControlInput
. - Реализация функции
pause
выполняется одним вызовом единственного метода, известного параметруinput
:pause
. - Классы
Keyboard
,XboxGamePad
иPlayStationGamePad
, теперь соответствующие GameControlInput, должны предоставлять свои собственные и правильные реализации методаpause
!InputController
не имеет к этому никакого отношения.
Теперь, когда OCP соблюдается, давайте справимся с новым требованием!
Нам больше не нужно изменять функцию pause
!
Что нам нужно сделать, это просто: создать YGamePad
класс, соответствующий интерфейсу GameControlInput
, и реализовать там функцию pause
!
Когда OCP соблюдается и в вашей системе планируется расширение, вам нужно только добавить новый код для выполнения задачи, в то время как старый остается нетронутым. Код, который написан один раз, хорошо спроектирован и не требует изменений, вряд ли со временем станет плохим кодом.
Более того, с точки зрения клиента, когда он просит добавить новую функцию, он на самом деле думает, что мы добавляем, а не изменяем старую!
Заключение
OCP великолепен, но не идеален.
Дизайн соответствия OCP не нацелен на то, чтобы сделать систему невосприимчивой ко всем видам изменений, а только к тем, которые связаны с расширением поведения класса. Применение OCP при разработке кода помогает снизить влияние и минимизировать усилия команды разработчиков, но иногда неизбежны крупномасштабные модификации. Вот почему существуют другие принципы проектирования, которые помогают нам еще больше улучшать наш код.
«Принцип открытости-закрытости - моральный центр системной архитектуры. И хотя моральное совершенство никогда не будет достигнуто, к нему, безусловно, стоит стремиться ».
- Роберт С. Мартин, он же дядя Боб
Наша задача как разработчика - знать, понимать и применять другие принципы S.O.L.I.D в нашем коде для повышения его качества.
Следующая статья: третье письмо от S.O.L.I.D - L для принцип замещения Лискова!
Надеемся, что сегодня вы приобрели что-то полезное с этим постом! Хлопайте нам в ладоши, если вам это понравилось, и не стесняйтесь делиться ими столько, сколько сможете! 🔥
Эта статья написана в соавторстве с Абдеррахим Бенмахлуфом.