Как вы определяете единую ответственность? - PullRequest
36 голосов
/ 29 октября 2008

Я знаю о "классе, имеющем единственную причину для изменения". Теперь, что это такое? Есть ли какие-то запахи / признаки, которые могли бы сказать, что у класса нет единой ответственности? Или реальный ответ может скрываться в YAGNI и отражать единственную ответственность при первом изменении класса?

Ответы [ 13 ]

2 голосов
/ 25 ноября 2008

Возможно, немного более технический, чем другие запахи:

  • Если вы обнаружите, что вам нужно несколько «друзей» классов или функций, это, как правило, хорошо пахнет плохим SRP - потому что требуемая функциональность на самом деле публично не раскрывается вашим классом.
  • Если в конечном итоге вы получаете чрезмерно «глубокую» иерархию (длинный список производных классов до тех пор, пока не доберетесь до конечных классов) или «широкую» иерархию (многие, многие классы являются производными от одного родительского класса). Обычно это признак того, что родительский класс делает слишком много или слишком мало. Ничего не делать - это предел, и да, я видел это на практике с «пустым» определением родительского класса, чтобы просто сгруппировать группу несвязанных классов в единую иерархию.

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

1 голос
/ 30 декабря 2009

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

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

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

Необходимо было применить определенное преобразование ко всем этим блокам кода (для тех, кто заинтересован: мне нужно было заменить концептуальные пользовательские переменные фактическими переменными реализации, чтобы предотвратить конфликты переменных. Те, кто знает макросы lisp, могут думать о gensym В бою :) ). Таким образом, самой простой вещью, которая работала бы, был посетитель, который знает операцию внутри и просто вызывает их в аннотированном кодовом блоке Action и Iteration при посещении и обходит все узлы синтаксического дерева. Однако в этом случае мне пришлось бы продублировать утверждение «преобразование применено» в моем тестовом коде для метода посещения и действия и метода посещения. Другими словами, мне пришлось проверять тесты продукта на наличие обязанностей Traversion (== {ход итерации, действие обхода, последовательность обхода}) x Преобразование (хорошо, преобразованный кодовый блок, который превратился в итерацию, преобразованную и преобразованное действие). Таким образом, у меня возникло желание использовать powermock, чтобы удалить метод преобразования и заменить его некоторым «return», я был преобразован! »; - Stub.

Однако, согласно практическому правилу, я разделил класс на класс TreeModifier, который содержит экземпляр NodeModifier, который предоставляет методы modifyIteration, modifySequence, modifyCodeblock и так далее. Таким образом, я мог легко проверить ответственность за обход, вызывая NodeModifier и реконструируя дерево, и тестировать фактическую модификацию блоков кода отдельно, тем самым устраняя необходимость в тестах продукта, потому что теперь обязанности были разделены (на обход и реконструкцию и конкретная модификация).

Интересно также отметить, что позже я мог бы многократно использовать TreeModifier в различных других преобразованиях. :)

0 голосов
/ 25 ноября 2008

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

Однажды я работал с унаследованным классом, у которого был метод "ZipAndClean", который явно архивировал и чистил указанную папку ...

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