Что говорят SOLID принципы?

 S O L I D — аббревиатура, объединяющая 5 принципов:

S - The Single Responsibility Principle (SRP) - принцип единственности ответственности

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

По книжке Дяди Боба (Роберта Мартина), классическое определение Single Responsibility принципа — одна и только одна причина может быть для изменения модуля или класса. Модуль или класс должен отвечать перед одним и только одним актором.

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

В противоположенность SRP можно поставить God Object - объект, который умеет все.

O - The Open Closed Principle (OCP) - принцип октрытости/закрытости

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

Открыты для расширения означает, что новые сущности могут создаваться путём создания новых типов сущностей. Мы можем изменять поведение (реализацию) существующих сущностей.

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

Создание дочерних элементов не должно приводить к изменению родительского кода.

Нарушение принципа OCP:

class Page {
  function buildElement($element) {
    if ($element->getType == 'div') {
     ...
    } else if ($element->getType == 'span') {
     ...
    }
  }
}

Код выше нарушает OCP - не закрыт для модификаций. Если появится новый тип, придется менять метод buildElement.

L - Liskov substitution (LSP) - принцип подстановки

Liskov Substitution Principle — объекты могут быть заменены экземплярами их подтипов (наследниками) без изменения правильности выполнения программы.

Примеры нарушения принципа LSP:

  1. В php 7.4 — можно нарушить этот принцип. В дочерних классах можно изменять типизацию родительских функций безнаказанно, типы стало можно сужать. Почему это разрешили в php 7.4? Разработчик теперь может сам решать, должнен ли соблюдаться принцип подстановки. А код позволяет сделать все.
  2. Если в наследники мы решим расширить метод родителя, но напишем все так плохо, что перед использованием метода нужно будет вызвать другой метод, например какой-нибудь configure(). Если configure() не был вызван (а ведь вызван не будет при замене парента на подкласс), то наш метод в наследнике уже будет работать как ожидается.
  3. Есть класс A c public свойством public $a; Если в наследнике B мы меняем область видимости на более строгую protected $a;, то мы получим Exception, в случае, если кто-то использовал это проперти. Это нарушение принципа LSP.

Еще одно определение LSP от Роберта Мартина:

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

I - Interface Segregation Principle - принцип разделения интерфейсов

Лучше иметь множество конкретных интерфейсов, чем один общий.

Интерфейс - для внешнего взаимодействия. Если интерфейс переусложненный, то взаимодействовать с ним тяжелее. Встает вопрос, нужны ли нам такие зависимости. Иметь God interface также плохо как и god object.

D - Dependency Inversion Principle - принцип инверсии зависимостей

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

Есть очень простой пример, позволяющий понять принцип. Например, есть сущность комментарий. Для сохранения комментария нам нужно соединение с базой данных. Так вот, если для сохранения комментария мы будем передавать mysqli_connection или pdo_connection или еще что-то в этом роде - то мы нарушим принцип инверсии зависимостей. Соединение с базой данных - в данном случае модуль низкого уровня. А комменатрий - модуль верхнего уровня. Таким образом для сохранения комментария нам нужно передавать абстрактный коннекшен, то есть интерфейс.

Комментарий не должен напрямую зависить от конкретного соединения, а должен завить от абстрактного DbConnection (это интерфейс). При следовании принципу инверсии зависимостей, если мы поменяем используемую базу данных c MySQL на PostgreSQL или другую, то нам не придется в классе, сохраняющем комментарий, менять реализацию. Комментарий продолжит сохраняться, а мы подменим только реализацию DbConnection.

 
 
 
 

icon Комментарии 0

Ваш комментарий к статье.. (для авторизованных)

ctrl+enter

icon Вход в систему

зарегистрироваться
НОВЫЕ ПОЛЬЗОВАТЕЛИ