Pull to refresh

Comments 16

ИМХО, это самая непонятная и запутанная попытка объяснить SRP, которые мне попадались :)
а что делать, если сам дядюшка Боб (Роберт Мартин) пишет, что понимание SRP запутано из-за его неудачного названия, из-за чего большинство думают, что каждый модуль должен отвечать за что-то одно. Этот принцип тоже есть и он тоже более чем оправдан, но под первым принципом SOLID подразумевается такая формулировка

«Модуль должен иметь одну и только одну причину для изменения. Пользователи и заинтересованные лица и есть та самая причина для изменений». (с) «Чистая архитектура», глава 7, страница 79
Эта фраза и была основной мотивацией написать эту статью, так как вторая ее часть еще более загадочна чем изначальное определение. На сайте у дядюшки Боба еще целую страницу он объясняет что значит «Пользователи и заинтересованные лица и есть та самая причина для изменений».
Переименовал статью в «Не такой простой, как кажется» ;)
Мне понравилось выступление на конференции Юнити, и там чел сказал что проверяет SRP очень просто, он задаёт вопрос — что делает этот класс? Если ответ содержит в себе список функционала, то вывод очевиден ).

Но на мой взгляд главное без глупого фанатизма.
Дополнил статью вашим замечанием.

Я написал эту статью в т.ч. из опыта общения с начинающими разработчиками и в процессе общения открыл для себя много нового.

Для тех кто уже знаком с Srp — да, вопрос «что делает этот класс» — хороший критерий для самопроверки.
Но если вас еще не успели познакомить — то возникает много вопросов:
1) При наличие фасадов ответ может быть расплывчатым
«Выпивоха ответственнен за все что касается выпивания». А стакан — касается выпивания или за хранения жидкости? Он часть выпивохи или нет? Для новичка (да и не только) это может быть большим вопросом.
2) Формулировка «за что отвечает» является больше мотивацией дробить чем объединять, то есть накладывает ограничение сверху по размеру модуля, по крайней мере в понимание начинающих программистов (практика опять таки показывает, что не только)

и там чел сказал что проверяет SRP очень просто, он задаёт вопрос — что делает этот класс? Если ответ содержит в себе список функционала, то вывод очевиден ).

Я, кстати, к этой проблеме захожу «от противного». Задаю вопрос: «сколько мест в программе нужно изменить, чтобы изменить поведение такого-то функционала». Если количество равно 1, то SRP соблюдён, по крайней мере, на необходимом и достаточном уровне.
А если у вас есть класс, который например что-то считает и пишет результат в файл, что, ИМХО, нарушение SRP. И, допустим, Вам нужно поменять алгоритм формирования имени файла(это уже может быть третьим ответ на вопрос «что?). Вы вроде как поменяете одно место, но „лишний“ функционал в классе останется…
Но с вашим замечанием про необходимый и достаточный уровень согласен.
Смотрите, я вот что имел в виду:
1. SRP, как и все остальные принципы, не есть самоцель. Это инструмент для упрощения сопровождения кода, и именно с этой позиции его надо применять.
2. Нет четкого определения, где надо дробить функционал на составляющие. Эта «черта сложности» как раз и есть регулятор, определяющий степень применения инструмента. Мы должны стараться её провести так, чтобы получить наилучшее, кхм, упрощение.
Соответственно, в данном примере, если при прочих равных условиях разделение этого класса на два приведет к усложнению сопровождения кода, просто проведите черту SRP выше него. Ваш класс теперь согласно условиям решает одну задачу расчета и вывода данных.
А вам не кажется что налить, выпить и закусить, для человека, это три части одного действия? И с точки зрения организма человека лишение хоть одного из них чревато ошибкой, а значит они явно связаны жестко между собой. Правильно ли бить на 3 разные сущности один жестко связанный процесс?
Зависит от контекста задачи. В случае с оригинальной студенческой игрой эти ответственности определенно разделены ;)

Если же весь процесс действительно жестко сцеплен, то есть изменение одной его части повлечет изменение другой — то это разделение не только не нужно, но и вредно.
В терминах SRP — такое разделение противоречит идее «локализации изменений».

Исключением может быть объемный процес, допустим на 500 строк кода (цифра «зависит»). В таком случае нужно стараться изыскивать возможности разделения ответственности, из-за ограничений нашего с вами мозга. Разбить сложный процесс на взаимодействие нескольких более простых.
Кстати, в контексте оригинальной студенческой задачи число игроков кратное 3 самый худший выбор, ибо нагрузка ложится на людей не равномерно, и если взять число игроков 4, то роли становятся задачами одной сущности, тоесть каждый должен уметь и наливать и пить и закусывать
К программированию эта нагрузка имеет мало отношения.
Если же волею ТЗ требуется взять именно 4 игрока, то разбиение выпивохи на сабличности тем более пригодится.
Мне статья показалась очень полезной. Автору спасибо! Интересно было бы еще почитать за остальные принципы SOLID. Единственное, у меня остался такой вопрос: если условно научить выпивоху по разному реагировать на напиток который ему необходимо выпить (стопку водки, или кружку пива), то есть действие «выпить залпом» / «выпить в течении времени». Вот это условие определяющее дальнейшее поведение должно находиться в декораторе или же правильнее вынести это разделение в непосредственно класс который овечат за потребление напитка? На мой взгяд этим должен заниматься декоратор, что вы думаете на этот счет?
Ответ: Это зависит. Если выпивание всегда одинаково, но есть много реализаций этого выпивания, то это либо наследование, либо декоратор, либо адаптер. Ифчики тоже никто не отменял. Семантически эти подходы почти не различаются, а какой из сахаров выбрать — зависит от контекста: языка, бизнеса и, пожалуй, фазы луны.

В любом случае — это не вопрос SRP. Это вопрос Open-Close принципа (OCP) и он требует отдельной проработки. Постараюсь найти время заняться и этим парнем ;)
Sign up to leave a comment.

Articles