Принципы проектирования SOLID
Принцип единой ответственности является первым и самым простым из принципов SOLIDдизайна; все же трудно получить право. Принципы SOLID – это набор принципов проектирования, призванных помочь разработчикам создавать хорошее, удобное в сопровождении, надежное и легко расширяемое программное обеспечение. Таким образом, хорошее понимание этих принципов необходимо для написания хорошего программного обеспечения.
SOLID — это аббревиатура пяти принципов дизайна.
1. S-принцип единой ответственности
2. O-принцип O-открытости-закрытости
3. L-принцип подстановки Лисков
4. I-принцип разделения интерфейсов
5. D-принцип инверсии зависимостей
Причина, по которой я пишу о принципе единой ответственности,несмотря на огромное количество статей в Интернете, предназначена для моего собственного назидания. Что может быть лучше для изучения сложных тем, чем писать об этом :). Надеюсь, поможет!
S — принцип единой ответственности
Принцип единой ответственности гласит, что каждый класс или модуль в программе должен выполнять одну единственную обязанность. Добавляя далее, элементы, компоненты ответственности должны быть инкапсулированы в один единственный класс, раскрывая только функциональность, необходимую для работы программы в целом.
Хм, выглядит просто, не так ли? Но как определить ответственность?
Роберт К. Мартин, разработчик SRP, описывает ответственность как «причину изменений». Другими словами, SRP можно сформулировать так:
У класса должна быть только одна причина для изменения.
Роберт К. Мартин, в просторечии известный как дядя Боб, в своей статье связывает SRP с термином связности Тома Демарко, который сравнивает различные элементы класса или модуля и насколько они релевантны друг другу и функциональности, которую выполняет класс или модуль. . Принимая это с принципом инкапсуляции, мы приходим к SRP; то есть все соответствующие элементы функциональности (ответственности) должны быть инкапсулированы вместе в одном классе/модуле, предоставляя только необходимые функции.
Суть SRP заключается в том, чтобы свести к минимуму влияние изменений, что в конечном итоге снизит стоимость обслуживания в течение жизненного цикла программного обеспечения. Думаю, сейчас слишком много теории. Перейдем к коду.
Давайте посмотрим на приведенный ниже код, который нарушает SRP. Это простая система управления тренажерным залом на питоне. Попробуем определить классы, их обязанности, какие проблемы с кодом.
Итак, мы видим, что наш класс GYMManager
делает совсем немного. Он добавляет новые customers
, добавляет packages
, print details
для клиентов и пакетов, а также управляет payment
. Хм... довольно много обязанностей для одного класса. Это классический пример «Объекта Бога». Он знает и делает слишком много. В настоящее время наш код довольно прямолинеен и прост. Но по мере добавления новых функций класс будет увеличиваться в размерах, и поддерживать его будет проблематично. Изменение одного метода может непреднамеренно повлиять на другие части кода. Например, если в будущем мы добавим новые способы оплаты, это может нарушить add_customer
метод. Таким образом, нарушение SRP не выглядит хорошим вариантом, хотя программа может работать, но в долгосрочной перспективе, безусловно, есть проблемы. Обязанности GYMManager могут быть определены как связанные с 1) customers
2) packages
и 3) payment
. Давайте запомним это и проведем рефакторинг.
Так что код на первый взгляд выглядит немного чище. Мы видим, что обязанности Customer
инкапсулированы в отдельный класс, то же самое касается классов Package
и Payment
. Если мы сейчас посмотрим повнимательнее, в этой версии кода мы увидим, что элементы, связанные с обязанностями/функциональными возможностями, выглядят инкапсулированными в один единственный класс. Теперь предположим, что мы хотим внести изменения в способ оплаты, мы можем сделать это, не затрагивая другие части кода. Если мы хотим добавить определенные новые атрибуты в класс клиентов, мы можем легко это сделать, не затрагивая другие классы. Таким образом, подключение к SRP на данный момент работает нормально. Давайте вернемся к определению SRP. У класса должна быть только одна причина для изменения или у него должна быть только одна обязанность. Рассмотрим Customer
класс. Что может быть причиной его изменения? Если мы хотим добавить новые атрибуты в класс Customer
, например, объекты, к которым у него есть доступ. Затем, что, если мы изменим способ печати сведений о клиенте. Итак, мы нашли не одну причину для изменения класса Customer
:(. Похоже, у класса Package тоже не одна причина для изменения.
А как насчет GYMManager
класса? Похоже ли, что у вас больше одной обязанности? Да. Это связано с добавлением новых клиентов и добавлением новых пакетов. Хм... так что еще есть над чем поработать. Проведем рефакторинг.
Фу! Много изменений. Таким образом, обязанности по печати деталей вынесены в отдельный класс. CustomerDetails
и PackageDetails
наследуются от этого класса, которые затем используются в классе GYMManager
. А GYMManager
просто действует как контейнер для разных объектов. Обязанности по добавлению новых клиентов и пакетов инкапсулированы в другой класс ListManager
. Затем CustomerListManager
и PackageListManager
наследуются от класса ListManager
, и их объекты используются классом GYMManager
.
Таким образом, мы эффективно определили различные обязанности и инкапсулировали их в разные классы. Эта версия кода является большим улучшением по сравнению с нашим «God Object», с которого мы начали. Он гибкий и ремонтопригодный. Мы значительно уменьшили вероятность того, что наш код непреднамеренно сломается в одном месте из-за изменений в другом.
Поздравляю!! если вы сделали это до сих пор. Надеюсь, вы узнали что-то о SRP.
Если у вас есть какие-либо вопросы или предложения, пожалуйста, не стесняйтесь оставлять их ниже в разделе комментариев.