Как вы определяете, насколько грубой или детальной должна быть «ответственность» при использовании принципа единой ответственности? - PullRequest
22 голосов
/ 16 марта 2010

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

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

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

Ответы [ 6 ]

27 голосов
/ 17 марта 2010

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

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


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

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

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

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

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

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

4 голосов
/ 16 марта 2010

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

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

Такой класс, как Repository, несет четкую и исключительную ответственность. Он имеет несколько методов, таких как «Сохранить и загрузить», но несет четкую ответственность за обеспечение постоянной поддержки сущностей «Персона». Класс также может координировать и / или абстрагировать обязанности зависимых классов, снова представляя это как отдельную ответственность перед другими потребляющими классами.

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

3 голосов
/ 13 декабря 2013

@ Дерик Бэйли: хорошее объяснение
Некоторые дополнения:
Вполне допустимо, что применение SRP является контекстной базой.
Вопрос все еще остается: есть ли объективные способы определить, нарушает ли данный класс SRP?

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

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

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

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

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

3 голосов
/ 16 марта 2010

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

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

3 голосов
/ 16 марта 2010

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

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

Разница невелика, но помогает мне. Давайте рассмотрим пример на википедии о движке создания отчетов:

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

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

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

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

2 голосов
/ 30 мая 2011

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

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

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

"одна причина для изменения"

или

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

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

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

Например:

class Modem
  send()
  receive()

Refactors to ==>

class ModemSender
class ModelReceiver

+

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

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

...