Какова область применения принципа единой ответственности и как он работает с DRY?

#design-patterns #domain-driven-design #solid-principles #object-oriented-analysis #single-responsibility-principle

#шаблоны проектирования #domain-driven-design #solid-принципы #объектно-ориентированный анализ #принцип единой ответственности

Вопрос:

Мне нужна помощь в разъяснении моего (неправильного) понимания принципа единой ответственности (SRP).

Во многих проектах, над которыми я работал, мои коллеги утверждают, что SRP означает, что класс должен реализовывать очень ограниченную функциональность, например, вычислять некоторые итоговые значения по коллекции объектов. Это приводит к классам, которые имеют один открытый метод (одна ответственность), например CalculateTotals(...) . Я не эксперт по DDD, но из того, что я видел, это приводит к анемичной модели предметной области, DTO и бесконечным микросервисам.

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

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

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

Если приведенное выше утверждение верно, означает ли это, что нам не нужно разбивать расчет налога на отдельный класс? Ну, мы могли бы сделать это, чтобы упростить код, но разве мы не сделали бы это по соображениям SRP?

Я ошибаюсь в своих мыслях?

Комментарии:

1. Было бы полезно, если бы вы добавили примеры классов. Не код, а общедоступные подписи. Как это выглядит сейчас и как вы хотите его изменить? Также было бы полезно, если бы вы добавили больше информации о фактическом расчете налогов

2. jgauffin, это скорее общий вопрос, чем конкретный случай. Я расстраиваюсь, когда смотрю на детали требования, а затем трачу так много времени, копаясь в бесконечных абстракциях и косвенных указаниях во имя SRP, чтобы найти реализацию. В основном это небольшой класс, охватываемый test. Все это чисто и легко понять, но кажется вырванным из контекста и несвязанным… Это случай, когда SRP доведен до нездоровой крайности или просто у меня кризис среднего возраста 😉

3. Тогда не приводите конкретный пример в вопросе.

Ответ №1:

Что на самом деле говорит принцип единой ответственности?

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

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

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

Какое отношение SRP имеет к DRY?

Обратное SRP заключается в том, что если два класса меняются по одной и той же причине (как в той игре в боулинг), возможно, они должны быть одним классом. Само по себе это не имеет никакого отношения к DRY (речь идет о распределении ответственности, а не о дублировании кода), но: если кто-то забыл DRY up при написании или извлечении второго класса, вы, вероятно, заметите, когда измените оба класса (или, что еще хуже, когда забудетеизменить один и выяснить в процессе производства) и напомнить, почему DRY важен.

Должен ли я дедуплицировать («ВЫСУШИВАТЬ») мои классы с единой ответственностью?

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

Однако всегда разъясняйте, как извлеченный метод соотносится с требованиями. Если вам нужно просуммировать некоторые значения одинаковым образом для отчетности и расчета налогов, не вызывайте метод totalForReporting или вызовите его total и поместите в свой, ReportingService а затем вызовите его из своего TaxService — тот, кто хочет изменить отчетность, а не расчет налогов, не подумает проверить, используется ли этот метод в контекстеэтого вы не ожидали от его названия. Вместо этого вызовите метод как-нибудь в общем виде transactionTotal или просто вызовите его total и поместите в свой GeneralLedgerService , и будет полностью (гм) ясно, какова его роль и когда он должен меняться или нет.

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