Практическое применение принципа открытости-закрытости и принципа инверсии зависимостей

Признаюсь, иногда, когда я оглядываюсь на код, который написал 2 года назад, я сам путаюсь. Или, что еще хуже, когда я хочу изменить одну часть кодовой базы и понимаю, что должен изменить все остальное, потому что моя кодовая база просто беспорядок.

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

В своей предыдущей статье я писал о MVC и MVVM в контексте iOS, и мы разработали отличное приложение PetManager. Но была большая проблема. На самом деле это был не PetManager, потому что он мог управлять только кошками. Честно говоря, для меня это было бы нормально, но я уверен, что есть много любителей собак и хомяков. Итак, в этой статье мы рассмотрим использование определенных принципов проектирования, чтобы сделать наше приложение более гибким для адаптации к таким изменениям.

Вы можете проверить код на https://github.com/wilfredbtan/PetManager и оформить заказ в ветке principles-base. Если вы хотите сразу перейти к готовому продукту, вы также можете проверить ветку принципы-решения.

Вот как должно выглядеть приложение до (слева) и после (справа):

А вот как выглядит наша текущая модель:

Если вы читали мою предыдущую статью, то заметили, что наше приложение теперь отслеживает статусы нашего кота (неудовлетворенный или мяукающий). Но проблема возникает, когда я внезапно проявляю интерес к собакам. Было бы круто иметь мяукающих собак, но это не тема этой статьи.

Теперь давайте попробуем добавить Собаки в нашу модель:

Ура, это работает! Хорошо, не совсем. Теперь предположим, что я хочу добавить в свою коллекцию хомячков и рыбок. Помимо ответственного владения домашними животными, это в основном сделает кодовую базу беспорядком. Основная проблема заключается в том, что мы должны продолжать модифицировать класс PetManager, и мне это пахнет плохим программированием.

Здесь мы можем применить принцип открытия-закрытия, принцип и принцип инверсии зависимостей.

Принцип открытого-закрытого и принцип инверсии зависимостей

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

Если мы сейчас посмотрим на код, то увидим, что нам нужно создать новый метод satifyDog() и getDogResponse(), если мы добавим класс собаки.

Но это нарушит OCP. Чтобы добавить новый тип питомца, не нарушая OCP, мы можем применить DIP. Вместо того, чтобы позволять PetManager (высокоуровневый) полагаться на модули Cat или Dog (низкоуровневые). Оба они могут полагаться на абстракцию (например, интерфейс PetResponse).

Вот как это будет выглядеть:

Мы видим, что, инвертировав зависимость, мы также удовлетворили OCP, поскольку теперь мы можем добавить больше ___Responses без изменения класса PetManagerModel. Но как насчет удовлетворения питомцев? (т. е. удовлетворять кошек() и удовлетворять собаку()). Имея оба класса, реализующие интерфейс Pet, мы можем просто реализовать метод satisfy(), и PetManagerModel нужно будет только вызвать его.

После применения OCP и DIP наша кодовая база будет выглядеть примерно так:

И мы это сделали, теперь у нас есть приложение, которое можно многократно использовать и расширять.

Вывод

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

Многие из вас, возможно, также поняли, что OCP и DIP — это просто подмножество принципов SOLID, которые были придуманы Майклом Фезерсом, но впервые представлены Робертом С. Мартином, также известным как дядя Боб. Я действительно рекомендую прочитать об этих принципах, так как они помогут вам начать свой путь к более совершенным методам программирования.

В этой статье в основном использовались примеры на Swift, но эти принципы определенно могут быть применены и к другим языкам. Попробуйте поиграть с приложением и узнайте, как сделать код более гибким. Например, что, если я хочу изменить тему приложения в зависимости от того, кошка это или собака? Как я могу поддержать добавление такой темы, не нарушая принципов, упомянутых выше? Дайте мне знать, как эта статья помогла вам в комментариях ниже: D

Удачного программирования!