Если заявления в функции, что является лучшим способом - PullRequest
9 голосов
/ 23 июня 2009

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

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

Для примера:

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();
}

Ответы [ 11 ]

8 голосов
/ 23 июня 2009

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

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

 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();
}
8 голосов
/ 23 июня 2009

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

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();
}
8 голосов
/ 23 июня 2009

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

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

2 голосов
/ 23 июня 2009

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

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

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

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

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

2 голосов
/ 23 июня 2009

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

// 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;
}
1 голос
/ 23 июня 2009

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

Статьи по переполнению стека :

0 голосов
/ 23 июня 2009

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

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

  1. Группируйте условия в логические единицы, когда это возможно
  2. Используйте четко названные промежуточные значения для упрощения чтения и отладки.
  3. Избегайте повторения одних и тех же логических конструкций более одного раза (см. Выше).
  4. По возможности, рефакторинг сложных или дорогих условий в отдельные функции.
  5. По возможности используйте для выхода условия раннего выхода в верхней части метода, но ...
  6. Избегайте перетекания операторов возврата по всему телу метода.
  7. Не полагайтесь на приоритет оператора, используйте скобки для группировки операторов.
  8. Старайтесь не полагаться на отступы, используйте блоки ({...}) даже с одним оператором, это поможет с ремонтопригодностью.
0 голосов
/ 23 июня 2009

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

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

0 голосов
/ 23 июня 2009

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

0 голосов
/ 23 июня 2009

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

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

Мои мысли по приведенным примерам:

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