Интерпретация принципа СУХОЙ

10

Прямо сейчас я борюсь с этим понятием СУХОЙ (не повторяй себя) в моем коде. Я создаю эту функцию, в которой я боюсь, что она становится слишком сложной, но я пытаюсь следовать принципу СУХОЙ.

createTrajectoryFromPoint(A a,B b,C c,boolean doesSomething,boolean doesSomething2)

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

Поэтому мой вопрос заключается в том, лучше ли иметь кучу различных функций, которые разделяют одну и ту же логику (что нарушает принцип СУХОЙ), или одну функцию, которая ведет себя немного по-разному, учитывая ряд параметров, но делая ее намного более сложной (но сохраняя СУХОЙ)?

Albinoswordfish
источник
3
Можно ли разделить общую / общую логику на частные функции, которые все разные публичные createTrajectory...функции вызывают?
FrustratedWithFormsDesigner
Это может быть, но эти частные функции все еще должны были бы получить те логические параметры
Albinoswordfish
2
Я думаю, что это будет / будет намного проще ответить на конкретном примере. Моя немедленная реакция заключается в том, что дихотомия, которую вы изображаете, не совсем реальна, т. Е. Это не единственные два варианта. Кроме того, я бы посчитал, что любое использование a booleanв качестве параметра в лучшем случае подозрительно.
Джерри Гроб
Связанный: Почему DRY важен?
Стивен Джеурис
Почему бы вам не выделить условные вещи в их собственные функции?
Рог

Ответы:

19

логические аргументы для запуска разных путей кода в одной функции / методе - ужасный запах кода .

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

Это означает, что вещи должны зависеть от других вещей только тогда, когда они должны ( Сцепление ), и что они должны делать одно и только одно (очень хорошо) ( Сплоченность ).

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

То, что ты делаешь, в любом случае не в духе СУХОГО. СУХОЙ больше о повторении ( Rрасшифровывается REPEAT). Избегать копирования и вставки является его основной формой. То, что вы делаете, не связано с повторением.

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

Сообщество
источник
Хорошо, похоже, что я пишу не очень хорошо (мое первоначальное подозрение), но я не совсем уверен, что это за «Sprit of DRY»
Albinoswordfish
4

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

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

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

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

Дима
источник
3

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

createTrajectoryFromPoint(A a,B b,C c){...}

dosomething(A a, B b, C c){...}

dosomething2(A a, B b, C c){...}

И теперь, передавая три одинаковых типа параметров трем различным функциям, вы снова будете повторять себя, поэтому вы должны определить структуру или класс, содержащий A, B, C.

В качестве альтернативы вы можете создать класс, содержащий параметры A, B, C и список операций, которые необходимо выполнить. Добавьте, какие операции (что-то, что-то2) вы хотите выполнить с этими параметрами (A, B, C), зарегистрировав операции с объектом. Затем есть метод для вызова всех зарегистрированных операций на вашем объекте.

public class MyComplexType
{
    public A a{get;set;}
    public B b{get;set;}
    public C c{get;set;}

    public delegate void Operation(A a, B b, C c);
    public List<Operation> Operations{get;set;}

    public MyComplexType(A a, B b, C c)
    {
        this.a = a;
        this.b = b;
        this.c = c   
        Operations = new List<Operation>();
    }

    public CallMyOperations()
    {
        foreach(var operation in Operations)
        {
            operation(a,b,c);
        }
    }
}
Мерт Акчакая
источник
Чтобы учесть возможные комбинации значений для логических параметров dosomething и dosomething2, вам потребуется 4 функции, а не 2, в дополнение к базовой функции createTrajectoryFromPoint. Этот подход плохо масштабируется, так как количество опций увеличивается, и даже присвоение имен функциям становится утомительным.
JGWeissman
2

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

Ryathal
источник
1

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

Во-первых, делай то, что сделал. Тяжело с СУХОЙ. Если вы не получите большой волосатый беспорядок, все готово. Если, как и в вашем случае, у вас нет повторяющегося кода, но каждое логическое значение проверяется в 20 различных местах, переходите к следующему шагу.

Во-вторых, разбить код на блоки. На логические значения ссылаются только один раз (ну, может быть, иногда два раза), чтобы направить выполнение к нужному блоку. С двумя логическими значениями вы получите четыре блока. Каждый блок практически идентичен. СУХОЙ нет Не делайте каждый блок отдельным методом. Это было бы более элегантно, но помещение всего кода в один метод облегчает или даже делает возможным для тех, кто занимается техническим обслуживанием, видеть, что они должны вносить каждое изменение в четырех местах. С хорошо организованным кодом и высоким монитором различия и ошибки будут почти очевидны. Теперь у вас есть поддерживаемый код, и он будет работать быстрее, чем исходный запутанный беспорядок.

В-третьих, попытайтесь собрать дубликаты строк кода в каждом из ваших блоков и превратить их в приятные, простые методы. Иногда ты ничего не можешь сделать. Иногда вы не можете сделать много. Но каждый маленький шаг, который вы делаете, возвращает вас к СУХОЙ и делает код чуть легче следовать и безопаснее поддерживать. В идеале ваш оригинальный метод может не иметь дублирующегося кода. В этот момент вы можете разделить его на несколько методов без логических параметров или нет. Удобство вызова кода в настоящее время является главной заботой.

Я добавил свой ответ к большому числу уже здесь из-за второго шага. Я ненавижу повторяющийся код, но если это единственный понятный способ решения проблемы, делайте это так, чтобы кто-нибудь сразу понял, что вы делаете. Используйте несколько блоков и только один метод. Сделайте блоки как можно более одинаковыми в именах, интервалах, выравниваниях ... во всем. Различия должны затем выскочить на читателя. Это может сделать очевидным, как переписать его в СУХОЙ манере, и если нет, то его поддержание будет достаточно простым.

RalphChapin
источник
0

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

createTrajectoryFromPoint(A a,B b,C c,IX x,IY y)

где у вас есть реализации IX и IY, которые представляют различные значения для логических значений. В теле функции, где бы вы ни находились

if (doesSomething)
{
     ...
}
else
{
     ...
}

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

JGWeissman
источник