Подтвердить что ты не робот

Как вы определяете единую ответственность?

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

4b9b3361

Ответ 1

Принцип единой ответственности

Существует много очевидных случаев, например. CoffeeAndSoupFactory. Кофе и суп в одном приборе могут привести к довольно неприятным результатам. В этом примере устройство может быть разбито на HotWaterGenerator и некоторая Stirrer. Затем из этих компонентов могут быть созданы новые CoffeeFactory и SoupFactory, и любое случайное смешение можно избежать.

Среди более тонких случаев напряжение между объектами доступа к данным (DAO) и объектами передачи данных (DTO) очень распространено. DAO разговаривают с базой данных, DTO сериализуются для передачи между процессами и машинами. Обычно DAO требуется ссылка на вашу базу данных, поэтому они непригодны для использования на ваших богатых клиентах, у которых не установлены драйверы базы данных и не имеют необходимых прав для доступа к БД.

Запах кода

  • Методы в классе начинают группироваться по областям функциональности ( "это методы Coffee, и это методы Soup" ).

  • Реализация многих интерфейсов.

Ответ 2

Напишите краткое, но точное описание того, что делает класс.

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

Ответ 3

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

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

Хороший способ остаться на дороге... Использовать объект как метафора человека... Если объектом был человек, кого бы я попросил сделать это? Назначьте ответственность перед соответствующим классом. Однако вы не просите одного и того же человека делать ваши файлы управления, вычислять зарплаты, выпускать зарплату и проверять финансовые записи... Почему вы хотите, чтобы один объект делал все это? (это нормально, если класс берет на себя множество обязанностей, если они все связаны и согласованы.)

  • Если вы используете CRC-карту, это хороший тонкий ориентир. Если у вас возникли проблемы с получением всех обязанностей этого объекта на CRC-карте, возможно, это слишком много... максимум 7 будет хорошим маркером.
  • Другим запахом кода из книги рефакторинга будут ОГРОМНЫЕ классы. Операция с дробовиком будет другой... внесение изменений в одну область в классе вызывает ошибки в несвязанных областях одного и того же класса...
  • Поиск того, что вы вносите изменения в один и тот же класс для несвязанных исправлений ошибок снова и снова, является еще одним признаком того, что класс делает слишком много.

Ответ 4

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

Ответ 5

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

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

Ответ 6

Очевидным признаком является то, что ваш класс выглядит как Big Ball of Mud, что на самом деле является противоположностью SRP (единственная ответственность принцип).

В принципе, все сервисы объектов должны быть сосредоточены на выполнении единой ответственности, что означает каждый раз, когда ваш класс меняется и добавляет услугу, которая не уважает это, вы знаете, что вы "отклоняетесь" от "правильного" пути; )

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

Ответ 7

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

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

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

Ответ 8

Если вы закончите с MethodA, который использует MemberA и MethodB, который использует MemberB, и он не является частью какой-либо схемы concurrency или версии, вы можете нарушить SRP.

Если вы заметили, что у вас есть класс, который просто делегирует вызовы во многие другие классы, вы можете застрять в адском прокси-класса. Это особенно верно, если вы в конечном итоге создаете экземпляр класса прокси-сервера, когда можете просто использовать конкретные классы напрямую. Я многое видел. Подумайте ProgramNameBL и ProgramNameDAL классы в качестве замены для использования шаблона репозитория.

Ответ 9

Вот некоторые вещи, которые помогают мне понять, нарушает ли мой класс SRP:

  • Заполните комментарии XML-документа к классу. Если вы используете такие слова, как if и, но, кроме, когда и т.д., Ваши классы, вероятно, делают слишком много.
  • Если ваш класс является службой домена, он должен иметь глагол в имени. Много раз у вас есть такие классы, как "OrderService", которые, вероятно, должны быть разбиты на "GetOrderService", "SaveOrderService", "SubmitOrderService" и т.д.

Ответ 10

Мартин Agile Principles, Patterns and Practices в С# помог мне много понять SRP. Он определяет SRP как:

У класса должна быть только одна причина для изменения.

Так что же заставляет меняться?

Ответ Мартина:

[...] каждая ответственность - это ось изменения. (стр. 116)

и далее:

В контексте СРП мы определяем ответственность за причину изменений. Если вы можете думать о более чем одном мотиве для изменения класса, этот класс несет более одной ответственности (p. 117)

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

Где находится YAGNI?

YAGNI может быть хорошо сочетается с SRP: когда вы применяете YAGNI, вы ждете, пока на самом деле не произойдет какое-то изменение. Если это произойдет, вы должны четко видеть обязанности, которые выводятся из причины (причин) для изменения.

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

Ответ 11

Я также пытался поднять принципы SOLID OOD, в частности принцип единой ответственности, например SRP (as побочная заметка подкаст с Джеффом Этвудом, Джоэлом Спольски и "Дядя Боб" заслуживает внимания). Для меня большой вопрос: какие проблемы SOLID пытается решить?

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

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

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

Ответ 12

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

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

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

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

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

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

Ответ 13

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

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