Принцип единой ответственности - злоупотребляю ли я этим?

13

Для справки - http://en.wikipedia.org/wiki/Single_responsibility_principle

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

  • Просмотр существующих записей главной книги в табличном формате.
  • Создать новую запись в книгу, используя кнопку создания.
  • Нажмите на запись в таблице (упоминается в первом указателе) и просмотрите подробности на следующей странице. Вы можете аннулировать запись в главной книге на этой странице.

(На каждой странице есть еще пара операций / проверок, но ради краткости я ограничусь ими)

Поэтому я решил создать три разных класса -

  • LedgerLandingPage
  • CreateNewLedgerEntryPage
  • ViewLedgerEntryPage

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

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

Tarun
источник
2
ИМХО, нет смысла пытаться ответить на ваш вопрос в целом, не зная двух вещей: насколько велики эти классы (по количеству методов и LOC)? И насколько больше / сложнее они будут расти в обозримом будущем?
Петер Тёрёк
2
10 методов каждый является ИМХО четким указанием не помещать код в один класс с 30 методами.
Док Браун
6
Это пограничная территория: класс из 100 LOC и 10 методов не слишком мал, а один из 300 LOC не слишком велик. Тем не менее, 30 методов в одном классе звучат слишком много для меня. В целом, я склонен согласиться с @Doc в том, что иметь меньше маленьких классов меньше риска, чем иметь один класс с избыточным весом. Особенно с учетом того, что занятия естественным образом имеют тенденцию прибавлять в весе с течением времени ...
Петер Тёрёк
1
@ PéterTörök - Я согласен, если код не может быть реорганизован в один класс, который может использоваться интуитивно, который повторно использует код вместо дублирования функциональности, как я ожидаю, что OP имеет.
SoylentGray
1
Возможно, применение MVC прояснит все это: три представления, одна модель (Ledger) и, возможно, три контроллера.
Кевин Клайн

Ответы:

19

Ссылаясь на @YannisRizos здесь :

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

Я не согласен, по крайней мере сейчас, после 30 лет опыта программирования. Меньшие классы ИМХО лучше поддерживать почти в каждом случае, о котором я могу думать в таких случаях, как этот. Чем больше функций вы поместите в один класс, тем меньше у вас будет структуры, и качество вашего кода ухудшается - каждый день немного. Когда я правильно понял Тарун, каждая из этих 3 операций

LedgerLandingPage

CreateNewLedgerEntryPage

ViewLedgerEntryPage

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

Если у вас есть общие функциональные возможности между этими 3 классами, они принадлежат либо к общему базовому классу, либо к Ledgerсамому классу (я полагаю, у вас есть один, по крайней мере, для операций CRUD), либо к отдельному вспомогательному классу.

Если вам нужно больше аргументов для создания множества небольших классов, я рекомендую заглянуть в книгу Роберта Мартина «Чистый код» .

Док Браун
источник
это единственная причина, по которой я придумал три разных класса вместо того, чтобы объединять все в одном классе. Так как среди них нет общей функциональности, у меня нет общего класса. Спасибо за ссылку.
Тарун
2
«Небольшие классы лучше поддерживать практически в каждом случае, о котором я могу подумать». Я не думаю, что даже Чистый код зашел бы так далеко. Вы действительно утверждаете, что в шаблоне MVC, например, должен быть один контроллер на страницу (или вариант использования, как вы выразились)? В рефакторинге у Фаулера два запаха кода: один ссылается на множество причин для изменения класса (SRP), а другой ссылается на необходимость отредактировать много классов для одного изменения.
фунтовые
@pdr, OP утверждает, что между этими классами нет общей функциональности, поэтому (для меня) трудно представить ситуацию, когда вам нужно коснуться более чем одного, чтобы сделать одно изменение.
Петер Тёрёк
@pdr: если у вас слишком много классов для редактирования за одно изменение, это, в основном, признак того, что вы не перенесли обычную функциональность в отдельное место. Но в приведенном выше примере это, вероятно, приведет к четвертому классу, а не только к одному.
Док Браун
2
@pdr: Кстати, вы действительно читали «Чистый код»? Боб Мартин идет очень далеко, разбивая код на более мелкие блоки.
Док Браун
11

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

Как пишет @Karpie в предыдущем ответе :

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

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

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

Аналогичный вопрос о чрезмерном использовании принципов дизайна, который, я думаю, вы найдете интересным: Шаблоны проектирования: когда использовать и когда прекратить делать все, используя шаблоны

Яннис
источник
1
SRP, насколько я понимаю, на самом деле не шаблон проектирования.
Док Браун
@DocBrown Конечно, не шаблон проектирования, но обсуждение вопроса чрезвычайно актуально ... Хотя, уловка, я обновил ответ
yannis
4

Давайте рассмотрим эти классы:

  • LedgerLandingPage: роль этого класса - показать список бухгалтерских книг. По мере роста приложения вы, вероятно, захотите добавить методы для сортировки, фильтрации, выделения и прозрачного объединения данных из других источников данных.

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

  • CreateLedgerEntryPage: Если у ViewLedgerEntryPageнего есть полная возможность редактирования, ответственность этого класса может быть выполнена путем редактирования «пустой записи», что может иметь или не иметь смысла.

Эти примеры могут показаться немного надуманными, но суть в том, что из UX мы говорим здесь о функциях. Отдельная особенность - это определенно отдельная ответственность. Поскольку требования к этой функции могут изменяться, а требования двух разных функций могут меняться в двух противоположных направлениях, и тогда вам хотелось бы, чтобы вы застряли с SRP с самого начала.
Но, наоборот, вы всегда можете разделить фасад на три класса, если по какой-либо причине вам удобнее иметь один интерфейс.


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

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

back2dos
источник
Думаю, даже я не мог бы описать ответственность этих классов лучше, чем вы.
Тарун
2

У этих классов есть только одна причина измениться?

Если вы еще этого не знаете, то, возможно, вы попали в ловушку спекулятивного проектирования / чрезмерного проектирования (применено слишком много классов, слишком сложные шаблоны проектирования). Ты не удовлетворил ЯГНИ . На ранних стадиях жизненного цикла программного обеспечения (некоторые) требования могут быть неясными. Таким образом, направление изменений в настоящее время не видно для вас. Если вы понимаете это, держите это в одном или минимальном количестве классов. Однако это сопряжено с определенными обязательствами: как только требования и направление изменений станут яснее, вам необходимо переосмыслить текущий дизайн и выполнить рефакторинг, чтобы удовлетворить причину изменения.

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

Все дело в следующем:

  • Следите за драйверами для изменения.
  • Выберите гибкий дизайн, который может быть переработан.
  • Будьте готовы к рефакторингу кода.

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

Однако всегда есть фактор времени: для многих изменений у вас не будет времени, которое вам нужно. Рефакторинг требует времени и усилий. Я часто сталкивался с ситуациями, когда я знал, что должен был изменить дизайн, но из-за нехватки времени мне пришлось отложить его. Это произойдет, и этого нельзя избежать. Я обнаружил, что реорганизовать код под измененным кодом легче, чем через встроенный. YAGNI дает мне хорошее руководство: если сомневаетесь, сведите к минимуму количество классов и применение шаблонов проектирования.

Тео Ленндорф
источник
1

Есть три причины для вызова SRP в качестве причины для разделения класса:

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

Есть три причины отказаться от разделения класса:

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

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

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

Кейт Грегори
источник
приятно видеть другую точку зрения
Тарун
0

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

Марк Се
источник
Что ж, бухгалтерская книга - это функция в пользовательском интерфейсе, и есть много связанных с ней операций, которые можно выполнять на разных страницах.
Тарун
0

ИМХО, вы не должны использовать классы вообще. Здесь нет абстракций данных. Главные книги представляют собой конкретный тип данных. Методы для манипулирования и отображения их являются функциями и процедурами. Конечно, будет много часто используемых функций, таких как форматирование даты. Мало места для абстрагирования и скрытия каких-либо данных в классе в процедурах отображения и выбора.

Есть некоторый случай, чтобы скрыть состояние в классе для редактирования главной книги.

Независимо от того, злоупотребляете ли вы SRP, вы злоупотребляете чем-то более фундаментальным: злоупотребляете абстракцией. Это типичная проблема, порожденная мифическим представлением, что ОО - это нормальная парадигма развития.

Программирование на 90% конкретнее: использование абстракции данных должно быть редким и тщательно продуманным. Функциональная абстракция, с другой стороны, должна быть более распространенной, поскольку детали языка и выбор алгоритмов должны быть отделены от семантики операции.

Yttrill
источник
-1

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

sevenseacat
источник
2
Для меня класс «... менеджера» обычно показывает, что вы не можете потрудиться разбить его дальше. Чем руководит класс? Презентация, настойчивость?
Себастьян
-1. Помещение 3 или более вариантов использования в 1 класс - это именно то, чего пытается избежать SRP - и по уважительным причинам.
Док Браун
@sebastiangeiger Так что же делают три класса ОП? Презентация или настойчивость?
Seseseacat
@Karpie Я искренне надеюсь на презентацию. Конечно, пример не очень хороший, но я ожидаю, что «... Page» на веб-сайте делает что-то похожее на представление.
Себастьянгейгер
2
Фактически, вам нужен только один класс для всего приложения, с единственной обязанностью сделать пользователей счастливыми ;-)
Péter Török