Привет 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 для принцип замещения Лискова!

Надеемся, что сегодня вы приобрели что-то полезное с этим постом! Хлопайте нам в ладоши, если вам это понравилось, и не стесняйтесь делиться ими столько, сколько сможете! 🔥

Эта статья написана в соавторстве с Абдеррахим Бенмахлуфом.