С примерами в этой статье мы легко поймем принципы SOLID.

Давайте кратко представим солид для тех, кто никогда о нем не слышал.

Сектор разработки программного обеспечения растет день ото дня, и коды, написанные в этом секторе, могут иметь самые разные структуры. Становится все труднее читать, понимать и развивать эти различные структуры. На данный момент Принципы SOLID буквально призваны облегчить жизнь разработчиков программного обеспечения. Объектно-ориентированное программирование лежит в основе SOLİD Principles, что позволяет нам повысить удобочитаемость и создавать гибкие структуры кода.

Я думаю, что мы начинаем понимать принципы SOLID, поэтому давайте подробнее остановимся на SOLID.

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

1. Принцип единой ответственности

Согласно принципам единой ответственности, при разработке программного обеспечения каждый класс или метод должен нести единственную ответственность. Никогда нельзя делать слишком много работы.
«Делайте одно дело и извлекайте из этого максимум пользы». можно сказать об этом принципе. Соблюдая этот принцип, события, которые вызовут трудности у команды, компании и разработчика программного обеспечения, сводятся к минимуму после внесения изменений в проект.

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

Во втором примере, поскольку Update и Insert были написаны разными методами, применялся принцип Responsibility. Риск ошибки, которая может возникнуть при вызове метода, устранен. Удобочитаемость и гибкость увеличились. Изменения, которые будут внесены в код в будущем, были упрощены.

2. Принцип открытия/закрытия

Согласно принципу Open/Close, проекты должны быть открыты для добавления новых функций и закрыты для изменения их функций. То есть, когда вы добавляете в проект новую фичу, вы не должны менять написанный ранее код. Чтобы привести пример из повседневной жизни, у автомобиля есть 4 колеса и 1 рулевое колесо. Объект автомобиля не подлежит изменению, мы не можем сделать из 3-х колес, но мы можем улучшить автомобиль, добавив стереосистему.

Если появляется еще один тип счета-фактуры, нам нужно добавить еще одно условие else if в исходный код вышеуказанного метода GetInvoiceDiscount(), что нарушает принцип открытого закрытого.

Если мы хотим показать еще один пример.

Допустим, мы хотим добавить в приложение новую функцию.

Программист, который не знает или не заботится о принципе открытия/закрытия, будет добавлять новые функции, как в примере, или аналогично примеру. Добавление циклов if else в класс CustomerDal загрязняет код и затрудняет управление.

Что ж, давайте посмотрим, как сознательный программист, читающий эту статью, обновит проект и применит принцип Toggle.

  1. Разработчик должен абстрагировать класс CustomerDal с помощью класса ICustomerDal.
  2. При использовании классов перечисления следует соблюдать осторожность. Необходимо ли использовать класс enum, его следует хорошо проанализировать.
  3. Операция добавления должна быть написана с помощью ICustomerDal.
  4. Новшества, которые будут добавлены в проект, следует классифицировать отдельно, например: SqlCustomerDal, JpaCustomerDal, HibernateCustomerDal.

3. Принцип подстановки Лисков.

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

LSP обычно объясняют на примере квадрата и прямоугольника. если мы предполагаем отношения ISA между Square и Rectangle. Таким образом, мы называем «квадрат — это прямоугольник». Код ниже представляет отношение.

Выше приведен код Square. Обратите внимание, что Квадрат расширяет Прямоугольник.

В этом случае мы пытаемся установить отношение ISA между Square и Rectangle таким образом, чтобы вызов «Square is a Rectangle» в приведенном ниже коде начал вести себя неожиданно, если передается экземпляр Square. Ошибка утверждения будет выдана в случае проверки «Площади» и проверки «Широты», хотя программа завершится, поскольку ошибка утверждения будет выдана из-за сбоя проверки области.

Класс демонстрирует принцип подстановки Лискова (LSP). Согласно этому принципу, функции, использующие ссылки на базовые классы, должны иметь возможность использовать объекты производного класса, не зная об этом.

Таким образом, в примере, показанном ниже, функция calculateArea, которая использует ссылку «Прямоугольник», должна иметь возможность использовать объекты производного класса, такого как Квадрат, и выполнять требование, предъявляемое определением Прямоугольника. Следует отметить, что в соответствии с определением прямоугольника всегда должно выполняться следующее, учитывая приведенные ниже данные:

  1. Длина всегда должна быть равна длине, переданной в качестве входных данных для метода setLength.
  2. Ширина всегда должна быть равна ширине, переданной в качестве входных данных для метода setBreadth.
  3. Площадь всегда должна быть равна произведению длины на ширину

В случае, если мы пытаемся установить связь ISA между Square и Rectangle таким образом, что мы вызываем «Square is a Rectangle», приведенный выше код начнет вести себя неожиданно, если будет передан экземпляр Square. В случае проверки области и проверки будет выдана ошибка утверждения. для широты, хотя программа завершится, так как будет выдана ошибка утверждения из-за сбоя проверки области.

Классу Square не нужны такие методы, как setBreadth или setLength. Класс LSPDemo должен знать подробности о производных классах Rectangle (например, Square) для правильного кодирования, чтобы избежать возникновения ошибки. Изменение существующего кода в первую очередь нарушает принцип открытого-закрытого.

4. Принцип разделения интерфейса

Принцип разделения интерфейса (ISP) гласит, что клиенты не должны зависеть от членов интерфейса, которые они не используют. Когда у нас есть несвязанные интерфейсы, интернет-провайдер направляет нас на создание нескольких, меньших, связанных интерфейсов.

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

В этом сценарии есть сотрудники компании и сотрудники компании, назначенные из других компаний. В то время как работники компании переопределяются с помощью Pay, Food, Work, Out Source Workers требуется переопределять только с помощью Pay, Work.

Самая большая ошибка здесь — оставить метод Food в OutSourceWorker пустым или вернуть null.

Этот неправильный подход можно исправить следующим образом.

5. Принцип инверсии зависимости

«Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций».

«Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций».

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

Изучив приведенный выше код, видно, что класс ExceptionReporter (класс высокого уровня) напрямую зависит от класса OracleDatabase (класс низкого уровня). К сожалению, если вы захотите в будущем использовать MySQL вместо Oracle в качестве базы данных, вам придется вмешаться в этот класс. Это нежелательное поведение. Решение этой проблемы состоит в том, чтобы абстрагировать зависимости здесь.

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

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

Спасибо за прочтение, в этой статье я постарался рассказать вам, что знаю, чему научился и что исследовал, надеюсь, она будет полезна.