Изучая принципы SOLID, многим людям трудно понять их значение и применение на практике. Цель этой статьи - раскрыть недоразумения относительно SOLID, с которыми я столкнулся, когда начал изучать принципы проектирования, а также те, которые я заметил у своих коллег. .
Твердые принципы
ТВЕРДЫЙ. представляет собой набор из 5 принципов, объединенных вместе и названных Робертом Мартином.
- Принцип единой ответственности
- Принцип открытости закрыт
- Принцип замены Лискова
- Принцип разделения интерфейса
- Принцип инверсии зависимостей
Следование этим принципам при написании кода значительно улучшает масштабируемость и ремонтопригодность программной системы. Понимание SOLID теперь требуется для успешного прохождения собеседования на должность разработчика в почти любой компании.
На SOLID есть много статей и видео, но, на мой взгляд, наиболее эффективный способ обучения - это объединить их с оригинальными формулировками и примерами из книг «Чистый код» и «Чистая архитектура» »Р. Мартина.
Сначала попробуем лучше понять SRP!
SRP - Тайна «Ответственности» и разложение
"Одинокий! Обязанность! Принцип! »- звучит довольно декларативно, не правда ли?
Когда на собеседовании вас просят рассказать об этом принципе, первое, что, вероятно, приходит в голову, это что-то, прямо связанное с названием принципа:
«Каждый объект в программе должен иметь только одну ответственность».
Сам Р. Мартин пишет в «Чистом коде», что SRP - это наиболее часто неправильно понимаемый принцип. Для меня главным препятствием к пониманию этого принципа было абстрактное слово «Ответственность».
Действительно, что такое «ответственность»?
Если немного отклониться в сторону абсурдной логики, окажется, что, например, игра, написанная в одном классе, несет ровно одну ответственность - она запускает игру!
Очевидно, что никому не пришло бы в голову применить такой пример на практике (хотя, конечно, бывают случаи), потому что SRP в первую очередь говорит о декомпозиции.
Но каков порог этого разложения?
Стоит ли при создании калькулятора описывать его 100 классами?
Или вот более конкретный, но не менее странный пример: следует ли мне создать отдельный класс для вывода на консоль, поскольку цель тега, с которым будет выводиться сообщение, и текст сообщения - разные «обязанности»?
Если мы продолжим эту цепочку рассуждений, мы получим борьбу неконтролируемого разложения и здравого смысла. Скорее всего, мы в конечном итоге решим, что понятие «ответственность» специфично для разработчика!
В упрощенном виде это может даже звучать так:
«Разлагайтесь, пока не почувствуете, что это только усложняет ситуацию!»
К счастью, формулировка SRP Р. Мартина совершенно иная.
Более того, в нем вообще нет слова «ответственность»!
«У класса должна быть одна и только одна причина для изменения» © R. Мартин.
Вместо «ответственности» мы получаем «причины для перемен»!
А теперь давайте определим источники изменений.
На уровне кода это зависимости наших классов. Дело в том, что следование принципу Open-Closed теоретически позволяет нам добавлять любые такие изменения без изменения существующего кода - например, путем создания новой реализации базового класса или интерфейса.
На уровне проекта это лица, заинтересованные в изменениях, такие как заказчик, менеджеры и т. Д.
Р. Мартин называет таких людей «актерами» и пишет, что SRP можно переформулировать так:
«Класс должен отвечать только за одного актера». © Р. Мартин.
Принцип единой ответственности говорит нам не об абстрактной «ответственности», а о том, что зависимости, используемые классом, должны изменяться по той же причине, а сам класс должен нести ответственность за единственного «актера».
Также обратите внимание на формулировку «одна и только одна причина».
SRP сообщает нам не только о том, что он намеревается сократить количество источников изменений, но и о том, что изменение логики по той же причине должно быть объединено.
Резюме
Таким образом, SRP не просто устанавливает «курс декомпозиции», но декларирует правило группировки сущностей в коде:
То, что меняется по обычным причинам, должно быть объединено, а то, что изменяется по разным причинам, должно быть разделено.
Задача разработчика при проектировании - определить источники и вес возможных изменений, как на основе формальной оценки по количеству и сложности зависимостей, так и по специфике проекта и его заинтересованных сторон.