Принципы проектирования 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.

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