Паттерны
September 25, 2023

Принципы разработки SOLID

SOLID — аббревиатура из пяти принципов ООП, которые позволяют разработчикам общаться на одном языке паттернов.

Single responsibility (SPR, принцип единственной ответственности)

О принципе:

Один класс решает только одну задачу, все необходимое должно инкапсулировано внутрь.

Решение проблем:

  • Большая связанность кода
    • Создание сложных супер-классов с разнонаправленной логикой и множеством зависимостей
    • Невозможно определить четкую ответственность класса или используемых методов
    • Каскадные поломки связанных методов
  • Сложности совместной разработки
    • Ухудшение читабельности
    • Сложность внесения изменений (дорого и больно)
  • Сложности тестирования

Пример:

Есть родительский класс Database, который отвечает за сохранение данных в БД. Класс имеет методы для подключения, выполнения запросов и обработки результатов.

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

Open–сlosed (OCP, принцип открытости/закрытости)

О принципе:

Класс должны быть открыт для расширения, но закрыт для изменения. Новые методы должны быть доставлены в класс путем расширения, не изменяя исходный класс.

Решение проблем:

  • Сохранение стабильности системы при расширении возможностей
  • Необходимость дополнительного регресс-тестирования при расширении возможностей
  • Снижение вероятности создания багов

Пример:

Eсть родительский класс Database c методами выполнения операций (GET, POST, PUT, DELETE), в него нужно добавить новый метод обновления данных — PATCH

Используя принцип открытости/закрытости, вместо изменения уже существующего метода обновления данных (PUT) мы должны добавить новый метод (PATCH).

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

Liskov substitution (LCP, принцип подстановки Лисков)

О принципе:

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

Преимущества:

  • Поведение классов становится более предсказуемым и прозрачным

Решение проблем:

  • Несовместимость типов

Пример:

Eсть родительский класс Database и два наследника: DatabaseDEV и DatabasePROD

Если заменить DatabaseDEV на DatabasePROD, который принимает в себя класс Database, то система должна продолжать работать, иначе это приведет к нарушению принципу и.

Interface segregation (ISP, принцип разделения интерфейса)

О принципе:

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

Преимущества:

  • Избавляем класс от неактивных методов
  • Более предсказуемая работа — проще понять, какие методы в классе используются и почему
  • Делаем класс переиспользуемым

Пример:

Eсть функция, которая принимает и возвращает строку. Эта функция может принимать строку разных форматов, но каждый формат требует отдельной обработки.

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

Dependency inversion (DIP, Принцип инверсии зависимостей)

Зависимости высокоуровневых классов должны идти через абстракции к конкретным реализациям, а не наоборот.

Преимущества:

  • Уменьшение связанности между классами: классы работают вместе, не зная о внутренних реализациях друг друга
  • Четкое разделение ответственности: упрощение тестирования, отладки, рефакторинга

Пример:

Eсть система бронирования билетов, она состоит из двух сервисов: сервис управления БД и бизнес-логика бронирования.

Если бизнес-логика будет реализована под архитектуру определенной БД, то изменение реализации сервиса БД приведет к потребности изменения реализации бизнес-логики бронирования.

Применяя DIP мы можем создать абстракцию в виде REST API, через которую бизнес-логика бронирования будет общаться с БД.