Как вы определяете, насколько крупной или детальной должна быть «ответственность» при использовании принципа единой ответственности?

В SRP «ответственность» обычно описывается как «причина для изменения», так что каждый класс (или объект?) Должен иметь только одну причину, по которой кто-то должен пойти туда и изменить его.

Но если вы подойдете к этому вопросу до мелочей, вы можете сказать, что объект, складывающий два числа вместе, является ответственностью и возможной причиной для изменения. Следовательно, объект не должен содержать никакой другой логики, потому что это может вызвать другую причину для изменения.

Мне любопытно, есть ли там кто-нибудь, у кого есть какие-нибудь стратегии для «определения объема», принцип единственной ответственности, который немного менее объективен?


person Mark Rogers    schedule 16.03.2010    source источник
comment
См. вики-страницу по тегам.   -  person jaco0646    schedule 22.09.2019


Ответы (6)


все сводится к контексту того, что вы моделируете. Я подробно писал и представлял принципы SOLID, и я специально обращаюсь к вашему вопросу в своих обсуждениях единой ответственности.

Следующий текст впервые появился в выпуске Code Magazine за январь / февраль 2010 г. и доступен в Интернете по адресу " Разработка программного обеспечения SOLID, шаг за шагом "


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

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

Причина, по которой у класса должна быть одна причина для изменения, а не одна причина для существования, - это бизнес-контекст, в котором вы строите систему. Даже если две концепции логически различны, бизнес-контекст, в котором они нужны, может потребовать их объединения. Ключевой момент при принятии решения о том, когда следует изменить класс, основан не на чисто логическом разделении концепций, а, скорее, на восприятии концепции бизнесом. Когда бизнес-восприятие и контекст изменились, у вас есть причина сменить класс. Чтобы понять, какие обязанности должен иметь один класс, вам нужно сначала понять, какая концепция должна быть инкапсулирована этим классом и где вы ожидаете изменения деталей реализации этой концепции.

Возьмем, к примеру, двигатель автомобиля. Вы заботитесь о внутренней работе двигателя? Вы заботитесь о том, чтобы у вас были поршень, распределительный вал, топливная форсунка и т. Д. Определенного размера? Или вас волнует только то, что двигатель работает должным образом, когда вы садитесь в машину? Ответ, конечно, полностью зависит от контекста, в котором вам нужно использовать движок.

Если вы механик, работающий в автомастерской, вы, вероятно, заботитесь о внутренней работе двигателя. Вам необходимо знать конкретную модель, различные размеры деталей и другие характеристики двигателя. Если у вас нет этой информации, вы, скорее всего, не сможете обслуживать двигатель должным образом. Однако, если вы обычный человек, которому нужен только транспорт из пункта А в пункт Б, вам, скорее всего, не понадобится такая информация. Понятие отдельных поршней, свечей зажигания, шкивов, ремней и т. Д. Для вас почти бессмысленно. Вам важно только, чтобы у вашего автомобиля был двигатель и он работал правильно.

Пример двигателя напрямую затрагивает суть принципа единой ответственности. Контексты вождения автомобиля и обслуживания двигателя дают два разных представления о том, что должно, а что не должно быть единой концепцией - причиной для изменений. В контексте обслуживания двигателя каждая отдельная часть должна быть отдельной. Вам нужно закодировать их как отдельные классы и убедиться, что все они соответствуют их индивидуальным спецификациям. Однако в контексте вождения автомобиля двигатель - это единое понятие, которое не требует дальнейшего разбора. В этом случае у вас, скорее всего, будет один класс с именем Engine. В любом случае контекст определил, каково соответствующее разделение ответственности.

person Derick Bailey    schedule 17.03.2010
comment
Очень хорошо объяснено. Как и во всем, что касается разработки программного обеспечения, контекст является ключевым. - person Chris Nicola; 18.03.2010
comment
Ответственность также имеет разный уровень детализации (Контекст). Например. Класс, вероятно, сопоставлен с объектом домена, таким как Product, Order и т. Д. У класса Product есть только одна причина для изменения, заключающаяся в изменении объекта домена Product. Возможно, необходимо добавить к продукту дополнительные свойства. - person Jacky; 09.01.2013
comment
@Derick, очень красивая статья, которая разъясняет мне SOLID? Большое спасибо. - person Jacky; 09.01.2013
comment
Я понимаю, что это старая тема, но недавно я задал аналогичный вопрос. Означает ли это, что класс движка будет композицией отдельных объектов для каждого из внутренних компонентов? Когда вы меняете внутреннее устройство, изменение ограничивается классом этой части, а когда вы используете движок, интерфейс будет таким же, как у движка? - person Ryan; 14.10.2015
comment
Райан - если предположить, что вам нужно смоделировать внутреннее устройство двигателя, да. Однако реальный вопрос заключается в том, нужно ли вам моделировать внутреннее устройство двигателя. - person Derick Bailey; 15.10.2015
comment
Не могли бы вы помочь мне с этим вопросом, так как у меня проблема с SRP: stackoverflow.com/questions/56017036/ - person ILoveStackoverflow; 10.05.2019

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

Вопрос действительно в том, насколько вероятно, что вещи будут меняться вместе, а не в том, могут ли они измениться или нет.

Разница небольшая, но мне помогает. Давайте рассмотрим пример в wikipedia о механизме отчетов:

  • если вероятность того, что содержимое и шаблон отчета изменится одновременно, высока, это может быть одним компонентом, поскольку они явно связаны. (Также может быть два)

  • но если вероятность того, что содержимое изменится без шаблона, важна, тогда это должны быть два компонента, потому что они не связаны. (Было бы опасно иметь один)

Но я знаю, что это личная интерпретация SRP.

Еще мне нравится второй прием: «Опишите свой класс одним предложением». Обычно это помогает мне определить, есть четкая ответственность или нет.

person ewernli    schedule 16.03.2010
comment
+1, интересный ответ, я вроде как изначально его истолковал, но формулировка принципала кажется немного расплывчатой. Директор, кажется, постоянно требует замены слова «ответственность» различными фразами. - person Mark Rogers; 16.03.2010
comment
Я добавил второй трюк, который мне нравится. Я до сих пор полностью согласен с тем, что это расплывчато. Но разве это не относится к какому-либо принципу дизайна? Это не формула и не рецепт. - person ewernli; 16.03.2010
comment
Не могли бы вы помочь мне с этим вопросом, так как у меня проблема с SRP: stackoverflow.com/questions/56017036/ - person ILoveStackoverflow; 10.05.2019

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

Чтобы лучше понять это, вероятно, будет полезно четко различать, за что отвечает класс, и что делает метод. Метод должен «делать только одно» (например, складывать два числа, хотя для большинства целей «+» - это метод, который уже делает это), в то время как класс должен представлять единственную четкую «ответственность» своим потребителям. Это ответственность на гораздо более высоком уровне, чем метод.

У такого класса, как Repository, есть четкая и исключительная ответственность. Он имеет несколько методов, таких как Save и Load, но несет четкую ответственность за обеспечение поддержки сохраняемости для сущностей Person. Класс также может координировать и / или абстрагироваться от ответственности зависимых классов, снова представляя это как единую ответственность для других классов-потребителей.

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

person Chris Nicola    schedule 16.03.2010
comment
Но на самом деле это не дает ответа на вопрос, как я могу определить ответственность класса? (мой ответ тоже, кстати) - person ewernli; 16.03.2010
comment
Что ж, я полагаю, что это природа разработки программного обеспечения, она требует некоторого уровня интуиции и (не очень) здравого смысла. Если бы мы могли просто применить какое-то произвольное правило для достижения SRP, то на самом деле это не было бы дизайном и, вероятно, был бы сокращенный вариант ReSharper SRP; P. - person Chris Nicola; 16.03.2010
comment
Кроме того, я думаю, что важно отметить, что SOLID конкретно касается «принципов», а не «правил». - person Chris Nicola; 16.03.2010
comment
+1 Хорошие моменты, но похоже, что вы говорите, что обязанность директора быть расплывчатой, а не как правило. Принципал может быть чрезвычайно похож на правила, но при этом его слишком сложно концептуализировать в автоматизированной системе рефакторинга. Хотя очевидно, что SRP никогда не может быть полностью автоматизированным инструментом рефакторинга. - person Mark Rogers; 16.03.2010
comment
Верно, я определенно не имею в виду, что принцип должен быть расплывчатым, но обычно он более всеобъемлющий, чем базовое правило. Он предназначен для руководства, но не обязательно для точного описания того, как что-то должно быть сделано. - person Chris Nicola; 16.03.2010
comment
Интересно, может ли неправильное применение принципов SOLID на самом деле быть еще одним новым запахом кода. - person Warren P; 16.04.2010
comment
Я думаю, что их можно применять вслепую или изолированно. SOLID, в конечном итоге, включает в себя концепцию хороших проектов ООП API и в значительной степени касается разработки конструкций кода, которые легко понять, использовать и расширять другими. Это очень сложно сделать хорошо, и на то, чтобы все исправить, нужно время. Это не может быть сведено к блок-схеме. - person Chris Nicola; 12.09.2013

Я использую простое эмпирическое правило: уровень или степень детализации ответственности должен соответствовать уровню или степени детализации рассматриваемой «сущности». Очевидно, что цель метода всегда будет более точной, чем цель класса, службы или компонента.

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

person Adrian K    schedule 16.03.2010

@Derick bailey: красивое объяснение
Некоторые дополнения:
Совершенно приемлемо, что применение SRP является контекстной базой.
Остается вопрос: есть ли какие-либо объективные способы определить, нарушает ли данный класс SRP?

Некоторые контексты проектирования довольно очевидны (например, пример автомобиля Дерика), но в остальном контексты, в которых должно быть определено поведение класса, во многих случаях остаются нечеткими.

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

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

Я применил этот подход, и он дал мне хорошие результаты.

Но мои поиски «объективных способов определения классовой ответственности» все еще продолжаются.

person aknon    schedule 13.12.2013

Я с уважением не согласен, когда Крис Никола выше говорит, что «класс должен нести единую четкую« ответственность »перед своими потребителями.

Я думаю, что SRP - это хороший дизайн внутри класса, а не его клиентов.

Для меня не очень понятно, что такое ответственность, и доказательством этого является количество вопросов, которые возникают в связи с этой концепцией.

"единственная причина измениться"

or

"если в описании есть слово" и "то его нужно разбить"

возникает вопрос: а где же предел? В конце концов, у любого класса с двумя общедоступными методами есть две причины для изменения, не так ли?

Для меня истинный SRP приводит к паттерну Фасад, где у вас есть класс, который просто делегирует вызовы другим классам.

Например:

class Modem
  send()
  receive()

Refactors to ==>

class ModemSender
class ModelReceiver

+

class Modem
  send() -> ModemSender.send()
  receive()  -> ModemReceiver.receive()

Мнения приветствуются

person ejaenv    schedule 30.05.2011
comment
Я нахожу случай модема довольно интересным и склонен думать, что с точки зрения пользователя его лучше рассматривать как набор интерфейсов: ITransmitStream, IReceiveStream, ITransmitSocket, IReceiveSocket, ITransmitReceiveSocket и т. Д., Потому что есть ситуации, в которых можно пожелать для разделения аспектов передачи и приема устройства (например, можно соединить последовательный кабель, чтобы подключить считыватель штрих-кода и принтер к одному и тому же порту), но есть и другие случаи, когда важно, чтобы две половины устройства были вместе. - person supercat; 23.12.2011
comment
Вот почему SOLID - это больше, чем просто SRP. Например, что-то вроде open closed больше актуально для внутреннего и внешнего дизайна классов и API, но ответственность определенно говорит о том, что класс представляет для своих потребителей, я не понимаю, как вы можете интерпретировать это по-другому. Несмотря на это, я думаю, что если вы пытаетесь упростить SOLID до простых правил, вам будет трудно. Например, если SRP - это только классы фасадов, тогда какие классы действительно работают? - person Chris Nicola; 12.09.2013