Принцип открытости-закрытости (OCP) является одним из принципов SOLID. OCP утверждает, что объекты или сущности должны быть открыты для расширения, но закрыты для модификации. Это означает, что когда в системе появляется новое требование, нам следует не изменять существующий код, а расширять его.

Это может немного сбить с толку. Как создать класс, который нельзя изменить? Как мы это делаем?

В этой статье мы поговорим об этом и выясним, почему это важно сделать.

Аналогия

Эту концепцию может быть немного сложно понять. Итак, давайте воспользуемся аналогией, чтобы было легче понять.

Представьте, что у вас есть три кубика Lego, которые вы хотите собрать, как Lego A, Lego B и Lego C. После этого вы хотите соединить три кубика Lego вместе, как показано ниже.

Это невозможно, потому что в Lego A и B нет дыры, которую можно было бы заполнить концом Lego C.

Одно из возможных решений — перестроить LEGO A и LEGO B. Мы заменим верхний кирпич так, чтобы в центре (красная область) оставалось отверстие для LEGO A, а также для LEGO B. Как на картинке ниже.

При этом дыру можно заполнить Lego C и соединить ее. Представьте это как 3D-здание. Прошу прощения, потому что я не умею создавать 3D.

Теперь вопрос в том, что если мы знаем, что однажды Lego A и B нужно будет соединить, почему бы нам просто не сделать отверстие (красное) в Lego A и B с самого начала?

Если мы спроектируем Lego A и B с самого начала, учитывая возможность соединения, мы сделаем весь процесс намного короче. Например, как показано ниже.

Нет необходимости заново собирать LEGO A и B. Мы можем просто соединить их с Lego C. Мы можем даже соединить их с Lego D.

Можно даже использовать множество других вариантов.

Какова связь между этим и принципом Открытия-Закрытия?

Помните об основе принципа открытости-закрытости: объекты или сущности должны быть открыты для расширения, но закрыты для модификации.

В первом примере выше мы нарушаем этот принцип. Когда возникает потребность в новой функции, нам нужно изменить Lego A и Lego B, перестроив их. Поэтому его можно подключить к Lego C.

После этого мы прекрасно следуем этому принципу. Это видно по тому, как мы можем применять новые функции/возможности без необходимости изменять Lego A и B. Мы делаем так, чтобы Lego A и B можно было соединить с Lego C, D и так далее.

Я думаю, что принцип открытости-закрытости учит нас тому, что мы всегда должны оставлять возможности для улучшения при разработке чего-либо. Если мы продумаем это с самого начала, мы сможем получить множество преимуществ, таких как гибкость, удобство сопровождения и так далее.

Здесь я понимаю значение невозможности изменять код и открытости только для расширения. Нам просто нужно с начала освободить место для «улучшения в будущем».

Как это применяется в реальном случае разработки программного обеспечения?

Представьте, что вас наняли на работу в одну из компаний электронной коммерции. В настоящее время они расширяют масштабы своих приложений. Один из них — обновление своей платежной системы. Раньше они принимали платежи только через дебетовые и кредитные карты. Теперь они хотят добавить Payoneer (провайдеров онлайн-платежей) в качестве функции оплаты. Вот приблизительный обзор существующей системы.

Класс содержит функцию оплаты с параметром типа. Позже параметр будет заполнен выбранным способом оплаты (дебет/кредит). Блок-схема будет примерно такой:

Если мы вернемся к принципу открытости-закрытости, приведенная выше блок-схема явно нарушает этот принцип. Почему? Потому что, когда мы захотим добавить еще один вариант оплаты, нам обязательно придется изменить существующий код, добавив еще один вариант.

Изменяя код, который уже хорошо работает, есть вероятность, что мы его повредим. В новой реализации могут возникнуть человеческие ошибки или логические ошибки. Это очень неэффективно. Особенно когда вы работаете в команде, как вы это делаете, если нужно внедрить 5 новых способов оплаты? Вам приходится редактировать один и тот же файл вместе?

Также с точки зрения эффективности видно, что мы используем случай переключения, чтобы проверить, какой метод будет обработан. Представьте, насколько долгим будет процесс переключения, если будет реализовано много способов оплаты?

Следовательно, нам необходимо применять принцип открытости-закрытости, который просто означает разработку нашего кода так, чтобы он был открыт для будущих изменений/улучшений, намеренно оставляя для них место.

Мы можем сделать это с помощью интерфейсов. Интерфейс — это контракт, определяющий поведение класса. Контракт содержит соглашение об имени, типе данных, типе возвращаемого значения и т. д. Классы, реализующие интерфейс, соглашаются обеспечивать указанное поведение.

Мы можем использовать интерфейс PaymentGatewayInterface, чтобы указать поведение платежного шлюза. Интерфейс будет определять один метод с именем pay(). Тогда каждый способ оплаты будет реализовывать PaymentGatewayInterface. Это обеспечит одинаковое базовое поведение всех способов оплаты.

Такой подход позволяет легко добавлять новые способы оплаты в будущем. Нам просто нужно создать новый класс, реализующий PaymentGatewayInterface. Это позволяет троим заменять друг друга в местах, где требуется PaymentGatewayInterface.

В результате процесс оплаты становится проще, как показано ниже. Это также позволит нам легко добавлять другие способы оплаты в будущем. Нам просто нужно создать класс, соответствующий существующему контракту.

Поздравляем!

Добро пожаловать в конец урока! Поздравляю с тем, что уже многому научился.

Во-первых, вы узнали о принципе открытости-закрытости. Этот принцип гласит, что код должен быть открыт для расширения, но закрыт для модификации. Вы также научились использовать аналогии, которые помогут вам понять этот принцип.

Во-вторых, вы узнали, как применить принцип Open Close в реальном случае разработки приложений. Мы узнали, как изменить код в функции оплаты, чтобы следовать этому принципу.

Вы все еще в замешательстве?

Возможно, объяснение этой концепции до сих пор оставило у вас много вопросов. Но поверьте, это нормально. Вы поймете это со временем. Чтобы понять эту концепцию, вам также необходимо знать ее реализацию в коде. Для этого я написал еще одну статью специально о реализации кода. Вы можете получить к нему доступ через: https://mrezkys.medium.com/open-closed-principle-o-in-solid-practical-with-swift-ddcc3c5e5778

От писателя

Здравствуйте, разрешите представиться — я Мухаммад Резкий Сулихин. Мы подошли к концу этой статьи, и я искренне благодарю вас за то, что нашли время ее прочитать. Если у вас есть какие-либо вопросы или отзывы, свяжитесь со мной напрямую по электронной почте [email protected]. Я более чем рад получить ваше мнение, будь то мои навыки письма на английском языке или что-то еще, я могу ошибаться. Ваши идеи помогут мне расти.

С нетерпением ждем возможности связаться с вами в будущих статьях! Кстати, я мобильный разработчик, сейчас учусь в Apple Developer Academy. Я открыт для различных возможностей, таких как сотрудничество, внештатная работа, стажировки, неполный или полный рабочий день. Для меня было бы огромным счастьем изучить эти возможности.

До следующих встреч, сохраняйте любопытство и продолжайте учиться!