Если утверждения в функции, каков наилучший способ

StackOverflow https://stackoverflow.com/questions/1033093

Вопрос

Если у меня есть функция с кучей условных операторов, как лучше всего ее организовать?

Меня беспокоит то, что кто-то еще войдет в код и поймет, что происходит.Хотя пример прост, представьте, что условное выражение очень сложное.

Например:

public void function(string value, string value2)
{
    if (value == null)
        return;

    if (value2 == value)
        DoSomething();
}

или

public void function(string value, string value2)
{
    if (value != null)
    {
        if (value2 == value)
            DoSomething();
    }
}

или

public void function(string value, string value2)
{
    if (value != null && value2 == value)
        DoSomething();
}
Это было полезно?

Решение

Организуйте условия и поместите их в метод.

например, замените это:

 if( a& & n || c  && ( ! d || e ) && f > 1 && ! e < xyz ) { 
      // good! planets are aligned.
      buyLotteryTicket();
 } else if( ..... oh my ... ) { 
 }

В это:

if( arePlanetsAligned() ) { 
    buyLotteryTicket(); 
} else if( otherMethodHere() ) { 
   somethingElse();
}  

Таким образом, не имеет значения, какой стиль вы используете (1, 2 или 3), поскольку оператор if четко описывает, какое условие проверяется.Нет необходимости в дополнительных конструкциях.

Цель состоит в том, чтобы сделать код более понятным и самодокументируемым.Если вы используете объектно-ориентированный язык программирования, вы можете использовать объект для хранения состояния (переменные) и избегать создания методов, принимающих 5–10 параметров.

Это похожие вопросы:

Лучший способ избавиться от вложенных ifs

Есть ли альтернатива этому гиперидентифицированному коду?

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

Он показывает, как это преобразовать:

public String myFunc(SomeClass input)
{
    Object output = null;

    if(input != null)
    {
        SomeClass2 obj2 = input.getSomeClass2();
        if(obj2 != null)
        {
            SomeClass3 obj3 = obj2.getSomeClass3();
            if(obj3 != null && !BAD_OBJECT.equals(obj3.getSomeProperty()))
            {
                SomeClass4 = obj3.getSomeClass4();
                if(obj4 != null)
                {
                    int myVal = obj4.getSomeValue();
                    if(BAD_VALUE != myVal)
                    {
                        String message = this.getMessage(myVal);
                        if(MIN_VALUE <= message.length() &&
                           message.length() <= MAX_VALUE)
                        {
                            //now actually do stuff!
                            message = result_of_stuff_actually_done;
                        }
                    }
                }
            }
        }
    }
    return output;
}

в это:

if ( isValidInput() && 
    isRuleTwoReady() &&
    isRuleTreeDifferentOf( BAD_OBJECT ) &&
    isRuleFourDifferentOf( BAD_VALUE ) && 
    isMessageLengthInRenge( MIN_VALUE , MAX_VALUE ) ) { 
            message = resultOfStuffActuallyDone();
}

Другие советы

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

Я понимаю, что это не провал, но эта концепция по-прежнему актуальна.Мне очень не нравятся вложенные if заявления вообще.

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

public void function(string value, string value2)
{
    if (string.IsNullOrEmpty(value1)) throw new ArgumentNullException("value1", "value 1 was not set");
    if (string.IsNullOrEmpty(value2)) throw new ArgumentNullException("value2", "value 2 was not set");

    DoSomething();
}

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

// leave complex conditional code out, so that we can focus on the larger problem in the function
public void function(string value, string value2)
{
    if (MyDescriptiveTestName)
    {
        DoSomething();
    }
}

// encapsulate complex conditional code so that we can focus solely on it.
private bool MyDescriptiveTestName(string value, string value2)
{
    if (value != null && value2 == value)
    {
        return true;
    }
    return false;
}

Могу ли я порекомендовать книгу Чистый код Роберт С.Martin, который предоставляет отличный набор эвристик для написания читаемого и поддерживаемого кода.

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

public void function(string value, string value2)
{
    if (valuesAreValidAndEqual(value, value2))
    {
        DoSomething();
    }
}

private void valuesAreValidAndEqual(string value, string value2)
{
    return value != null && value2 == value;
}

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

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

Связанные статьи о переполнении стека:

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

То, что вы заранее думаете о читаемости кода, — это полдела.

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

Мое мнение по поводу приведенных примеров:

  • Лично я думаю, что первый пример легче всего следовать.
  • Минимизация уровней гнездования и/или количества условных условий обычно повышает читабельность.
  • Некоторые будут спорить против нескольких точек выхода из метода (например, пример 1), но я думаю, что t становится проблемой только тогда, когда метод становится действительно долго.На самом деле это не так уж важно, если вы просто проверяете входные данные и быстро терпят неудачу.

Я предпочитаю второй вариант.Лично, когда я читаю такой код, я имею в виду условия, при которых мне придется войти на каждый вложенный уровень.В первом примере я, вероятно, забыл бы, что первое условие (значение == null равно false) продолжает выполняться.Третий тоже неплох, но мне больше нравится второй.

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

Не существует ЛУЧШЕГО способа размещения утверждений if, я думаю, что ответы субъективны.

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

Вот мои личные практические правила при создании функции со множеством условных проверок:

  1. По возможности группируйте условия в логические единицы.
  2. Используйте четко названные промежуточные значения, чтобы упростить чтение и отладку.
  3. Избегайте повторения одних и тех же логических конструкций более одного раза (см. выше).
  4. По возможности реорганизуйте сложные или дорогостоящие условия в отдельные функции.
  5. Когда это возможно, используйте условия раннего выхода в верхней части метода для выхода, но...
  6. Избегайте использования операторов возврата по всему телу метода.
  7. Не полагайтесь на приоритет операторов, используйте круглые скобки для группировки операторов.
  8. Не полагайтесь на отступы, используйте блоки ({...}) даже с одним оператором, это упрощает сопровождение.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top