2008-08-08 2 views
28

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

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

Пример, все в одной строке:

if (var1 = true && var2 = true && var2 = true && var3 = true && var4 = true && var5 = true && var6 = true) 
{ 

примера, многострочный:

if (var1 = true && var2 = true && var2 = true 
&& var3 = true && var4 = true && var5 = true 
&& var6 = true) 
{ 

Пример вложенным:

if (var1 = true && var2 = true && var2 = true && var3 = true) 
{ 
    if (var4 = true && var5 = true && var6 = true) 
    { 

ответ

61

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

bool isOpaque = object.Alpha == 1.0f; 
bool isDrawable = object.CanDraw && object.Layer == currentLayer; 
bool isHidden = hideList.Find(object); 

bool isVisible = isOpaque && isDrawable && ! isHidden; 

if(isVisible) 
{ 
    // ... 
} 

еще лучше:

public bool IsVisible { 
    get 
    { 
     bool isOpaque = object.Alpha == 1.0f; 
     bool isDrawable = object.CanDraw && object.Layer == currentLayer; 
     bool isHidden = hideList.Find(object); 

     return isOpaque && isDrawable && ! isHidden; 
    } 
} 

void Draw() 
{ 
    if(IsVisible) 
    { 
     // ... 
    } 
} 

Убедитесь, что вы даете переменное имя, actualy указать намерение, а не функции. Это очень поможет разработчику поддерживать ваш код ... это может быть ВАС!

+0

Простая, легкие дела, и эффективную. – 2010-05-13 11:12:20

5

Во-первых, я бы удалить все == true детали, что сделало бы это на 50% короче;)

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

Иногда я использую De-Morgan's laws для упрощения булевых выражений.

1

Я прибегаю к отдельным булевым значениям:

Bool cond1 == (var1 && var2); 
Bool cond2 == (var3 && var4); 

if (cond1 && cond2) {} 
3

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

if (var1 == true 
    && var2 == true 
    && var3 == true 
    ) { 
    /* do something.. */ 
} 

Я обычно ставлю близкую скобку на ту же строку, последнее условие:

if (var1 == true 
    && var2 == true 
    && var3 == true) { 
    /* do something.. */ 
} 

Но я не думаю, что это так же чисто.

6

Я часто разделить эти вверх на составные логические переменные:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled; 
bool custValid = customerBalance == 0 && customerName != "Mike"; 
if (orderValid && custValid) 
{ 
... 
2

Ну, во-первых, почему бы не:

если (var1 var2 & & & & var2 & & var3 & & var4 & & var5 & & var6) {
...

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

Это не лучше, но то, что я делал в прошлом: (Следующий метод предотвращает короткое замыкание булевым тестированием, все тесты выполняются, даже если первая ложная. Не рекомендуется шаблон, если вы не знаете, что вам нужно чтобы всегда выполнять весь код перед возвратом - Спасибо ptomato за обнаружение моей ошибки!)

boolean ok = cond1;
ok & = cond2;
ok & = cond3;
ОК & = cond4;
ОК & = cond5;
ok & = cond6;

Что такое же, как: (не то же самое, смотри выше примечание!)

ок = (cond1 & & cond2 & & cond3 & & cond4 & & cond5 & & cond6) ;

+0

Это не то же самое, если оператор `&&` является короткозамкнутым. – ptomato 2010-05-10 15:57:33

+0

Ничего себе. Я должен был это знать. Мой первый facepalm на одном из моих собственных ответов ;-) – 2010-05-13 10:43:17

0

Я хотел бы разбить их на уровне, так что я бы форматировать вам пример, как это:

if (var1 = true 
&& var2 = true 
&& var2 = true 
&& var3 = true 
&& var4 = true 
&& var5 = true 
&& var6 = true){ 

Это удобно, когда у вас есть больше вложенности, как это (очевидно, реальные условия будут более интересно, чем "= True" для всего):

if ((var1 = true && var2 = true) 
&& ((var2 = true && var3 = true) 
    && (var4 = true && var5 = true)) 
&& (var6 = true)){ 
4

Заканчивать Implementation Patterns Кент Бек. Есть конкретная модель, о которой я думаю, которая может помочь в этой ситуации ... она называется «Гвардия». Вместо того, чтобы иметь массу условий, вы можете разбить их на охрану, что дает понять, какие неблагоприятные условия в методе.

Так, например, если у вас есть метод, который делает что-то, но есть определенные условия, при которых он не должен делать что-то, а не:

public void doSomething() { 
    if (condition1 && condition2 && condition3 && condition4) { 
     // do something 
    } 
} 

Вы можете изменить его:

public void doSomething() { 
    if (!condition1) { 
     return; 
    } 

    if (!condition2) { 
     return; 
    } 

    if (!condition3) { 
     return; 
    } 

    if (!condition4) { 
     return; 
    } 

    // do something 
} 

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

Я НАСТОЯЩИМ рекомендую эту книгу кстати.

+0

«Быстрое возвращение» убивает «стрелку головы» против шаблона :) – 2010-05-13 18:29:53

+0

Я не согласен с тем, что эти гвардейцы облегчают чтение. Комментарий к «сложному» состоянию будет лучше. – 2017-12-20 22:10:17

12

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

http://www.refactoring.com/catalog/decomposeConditional.html

+3

Мне не нравится Decompose Conditional, потому что он загрязняет структуру кода одноразовыми функциями, которые не могут использоваться повторно.Я предпочел бы иметь большой оператор IF с комментариями для каждой «группы» связанных проверок. – 2010-05-09 12:03:28

7

Есть два вопроса по решению проблемы здесь: читабельности и понятности

Решение «читаемость» является вопросом стиля и как таковой является открытым для интерпретации , Я предпочитаю это:

if (var1 == true && // Explanation of the check 
    var2 == true && // Explanation of the check 
    var3 == true && // Explanation of the check 
    var4 == true && // Explanation of the check 
    var5 == true && // Explanation of the check 
    var6 == true) // Explanation of the check 
    { } 

или это:

if (var1 && // Explanation of the check 
    var2 && // Explanation of the check 
    var3 && // Explanation of the check 
    var4 && // Explanation of the check 
    var5 && // Explanation of the check 
    var6) // Explanation of the check 
    { } 

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

/// <Summary> 
/// Tests whether all the conditions are appropriately met 
/// </Summary> 
private bool AreAllConditionsMet (
    bool var1, 
    bool var2, 
    bool var3, 
    bool var4, 
    bool var5, 
    bool var6) 
{ 
    return (
     var1 && // Explanation of the check 
     var2 && // Explanation of the check 
     var3 && // Explanation of the check 
     var4 && // Explanation of the check 
     var5 && // Explanation of the check 
     var6); // Explanation of the check 
} 

private void SomeMethod() 
{ 
    // Do some stuff (including declare the required variables) 
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6)) 
    { 
     // Do something 
    } 
} 

Теперь при визуальном сканировании метод «SomeMethod», фактическая сложность тестовой логики скрыта, но смысловое значение сохраняется для людей, чтобы понять, в высокого уровня. Если разработчику действительно нужно разобраться в деталях, можно изучить метод AreAllConditionsMet.

Это, как известно, формальный рефакторинг «Decompose Conditional». Инструменты, такие как Resharper или Refactor Pro! может сделать этот вид рефакторинга простым!

Во всех случаях ключ к чтению и понятному коду заключается в использовании реалистичных имен переменных. Хотя я понимаю, что это надуманный пример: «var1», «var2» и т. Д. не допустимые имена переменных. Они должны иметь имя, которое отражает основной характер данных, которые они представляют.

0

Если вам случится быть программирования в Python, это подпруга с встроенной all() функции, примененной по списку ваших переменных (я буду использовать булевы литералы здесь):

>>> L = [True, True, True, False, True] 
>>> all(L) # True, only if all elements of L are True. 
False 
>>> any(L) # True, if any elements of L are True. 
True 

Есть любая соответствующая функция на вашем языке (C#? Java?). Если это так, это, пожалуй, самый чистый подход.

-2

Если вы сделаете это:

if (var1 == true) { 
    if (var2 == true) { 
     if (var3 == true) { 
      ... 
     } 
    } 
} 

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

+3

Возможно, это худшее решение этого вопроса. – 2008-10-14 20:59:45

+1

Горизонтальная полоса прокрутки - активируется !!! 11eleven – 2010-05-13 18:34:19

0

Макдауэлл,

Вы правы, что при использовании одного «&» оператора, что обе стороны выражения оценки. Однако при использовании оператора '& &' (по крайней мере, на C#) первое выражение для возврата false является последним выраженным выражением. Это приводит к тому, что evaulation ставит перед заявлением FOR так же хорошо, как и любой другой способ сделать это.

1

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

0

@tweakt

Это не лучше, но то, что я сделал в прошлом:

булевой ок = COND1; ok & = cond2; ok & = cond3; ok & = cond4; ok & = cond5; ok & = cond6;

который является таким же, как:

ок = (cond1 & & cond2 & & cond3 & & cond4 & & cond5 & & cond6);

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

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

do { 
    if (!cond1) 
     break; 
    if (!cond2) 
     break; 
    if (!cond3) 
     break; 
    ... 
    DoSomething(); 
} while (false); 

это время (ложь) является своего рода сырный. Я бы хотел, чтобы на языках появился оператор под названием «один раз» или что-то, что вы могли легко вырваться из него.

2

Попробуйте взглянуть на функторы и предикаты. Проект Apache Commons имеет большой набор объектов, позволяющих инкапсулировать условную логику в объекты. Пример их использования доступен на O'reilly here. Отрывок из примера кода:

import org.apache.commons.collections.ClosureUtils; 
import org.apache.commons.collections.CollectionUtils; 
import org.apache.commons.collections.functors.NOPClosure; 

Map predicateMap = new HashMap(); 

predicateMap.put(isHonorRoll, addToHonorRoll); 
predicateMap.put(isProblem, flagForAttention); 
predicateMap.put(null, ClosureUtils.nopClosure()); 

Closure processStudents = 
    ClosureUtils.switchClosure(predicateMap); 

CollectionUtils.forAllDo(allStudents, processStudents); 

Теперь подробность всех этих isHonorRoll предикатов и затворы, используемые для их оценок:

import org.apache.commons.collections.Closure; 
import org.apache.commons.collections.Predicate; 

// Anonymous Predicate that decides if a student 
// has made the honor roll. 
Predicate isHonorRoll = new Predicate() { 
    public boolean evaluate(Object object) { 
    Student s = (Student) object; 

    return((s.getGrade().equals("A")) || 
      (s.getGrade().equals("B") && 
       s.getAttendance() == PERFECT)); 
    } 
}; 

// Anonymous Predicate that decides if a student 
// has a problem. 
Predicate isProblem = new Predicate() { 
    public boolean evaluate(Object object) { 
    Student s = (Student) object; 

    return ((s.getGrade().equals("D") || 
       s.getGrade().equals("F")) || 
      s.getStatus() == SUSPENDED); 
    } 
}; 

// Anonymous Closure that adds a student to the 
// honor roll 
Closure addToHonorRoll = new Closure() { 
    public void execute(Object object) { 
    Student s = (Student) object; 

    // Add an award to student record 
    s.addAward("honor roll", 2005); 
    Database.saveStudent(s); 
    } 
}; 

// Anonymous Closure flags a student for attention 
Closure flagForAttention = new Closure() { 
    public void execute(Object object) { 
    Student s = (Student) object; 

    // Flag student for special attention 
    s.addNote("talk to student", 2005); 
    s.addNote("meeting with parents", 2005); 
    Database.saveStudent(s); 
    } 
}; 
0

Я хотели разбить каждое условие в описательные переменные.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid; 
isVar1Valid = (var1 == 1) 
isVar2Valid = (var2.Count >= 2) 
isVar3Valid = (var3 != null) 
isVar4Valid = (var4 != null && var4.IsEmpty() == false) 
if (isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid) { 
    //do code 
} 
2

совет Стива Mcconell, от Code Complete: Используйте многомерную таблицу. Каждая переменная служит индексом для таблицы, и оператор if превращается в поиск таблицы. Например, если (размер == 3 & & вес> 70) переводится в решение ввода таблицы [размер] [weight_group]

0

Если бы я делал это в Perl, это как я мог бы запустить проверку.

{ 
    last unless $var1; 
    last unless $var2; 
    last unless $var3; 
    last unless $var4; 
    last unless $var5; 
    last unless $var6; 

    ... # Place Code Here 
} 

Если вы планируете использовать это по подпрограмме заменить каждый экземпляр last с return;

1

В отражательных языках, как PHP, вы можете использовать переменные-переменные:

$vars = array('var1', 'var2', ... etc.); 
foreach ($vars as $v) 
    if ($$v == true) { 
     // do something 
     break; 
    } 
0
if ( (condition_A) 
     && (condition_B) 
     && (condition_C) 
     && (condition_D) 
     && (condition_E) 
     && (condition_F) 
     ) 
    { 
     ... 
    } 

в отличие от

if (condition_A) { 
     if (condition_B) { 
      if (condition_C) { 
      if (condition_D) { 
       if (condition_E) { 
        if (condition_F) { 
         ... 
        } 
       } 
      } 
      } 
     } 
    } 

и

if ( ( (condition_A) 
      && (condition_B) 
      ) 
     || ( (condition_C) 
      && (condition_D) 
      ) 
     || ( (condition_E) 
      && (condition_F) 
      ) 
     ) 
    { 
     do_this_same_thing(); 
    } 

в отличие от

if (condition_A && condition_B) { 
     do_this_same_thing(); 
    } 
    if (condition_C && (condition_D) { 
     do_this_same_thing(); 
    } 
    if (condition_E && condition_F) { 
     do_this_same_thing(); 
    } 

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

Вертикальное выравнивание с одинаковым уровнем отступов открытия/закрытия {}, открывание закрывающей скобки(), условные выражения с круглыми скобками и операторы слева - очень полезная практика, которая значительно повышает читаемость и ясность кода как против заклинивания всего, что может быть зажато на одной линии, без вертикального выравнивания, пробелов или скобок

Правила приоритета операторов сложны, например & & имеет более высокий приоритет, чем ||, но | имеет приоритет, чем & &

Итак, ...

if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H { 
    } 

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

if ( ( (expr_A) 
      & (expr_B) 
      ) 
     || ( (expr_C) 
      | ( (expr_D) 
       & (expr_E) 
      ) 
      ) 
     || ( (expr_E) 
      && ( (expr_F) 
       & (expr_G) 
       ) 
      ) 
     || (expr_H) 
     ) 
    { 
    } 

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

Смежные вопросы