Как вы справляетесь с огромными 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)
     {
8.08.2008 16:45:58
21 ОТВЕТ
РЕШЕНИЕ

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

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)
     {
         // ...
     }
}

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

61
8.08.2008 16:58:43
Просто, легко и эффективно.
Graham Clark 13.05.2010 11:12:20

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

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

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

5
25.06.2012 12:09:49

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

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

if ( cond1 && cond2 ) {}
1
8.08.2008 16:49:54

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

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

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

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

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

3
8.08.2008 16:50:23

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

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...
6
8.08.2008 16:50:57

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

if (var1 && var2 && var2 && var3 && var4 && var5 && var6) {
...

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

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

логическое ok = cond1;
хорошо & = cond2;
хорошо & = cond3;
хорошо & = cond4;
хорошо & = cond5;
хорошо & = cond6;

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

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

2
13.05.2010 10:45:25
Это не то же самое, если &&оператор короткого замыкания.
ptomato 10.05.2010 15:57:33
Вау. Я должен был знать это. Мой первый лицевой ответ на один из моих собственных ответов ;-)
Mark Renouf 13.05.2010 10:43:17

Мне нравится разбивать их по уровням, поэтому я отформатирую ваш пример так:

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)){
0
8.08.2008 16:59:56

Проверьте Образцы Реализации Кентом Беком. Существует определенная схема, которая, я думаю, может помочь в этой ситуации ... она называется «Страж». Вместо того, чтобы иметь кучу условий, вы можете разбить их на охрану, что дает понять, какие условия являются неблагоприятными в методе.

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

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
}

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

Кстати, я очень рекомендую эту книгу.

4
8.08.2008 17:04:33
«Быстрое возвращение» также убивает «паттерна со стрелами» :)
Arnis Lapsa 13.05.2010 18:29:53
Я не согласен с тем, что эти гвардейцы облегчают чтение. Комментарий о «сложном» состоянии был бы лучше.
Randy Stegbauer 20.12.2017 22:10:17

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

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

12
13.05.2010 18:16:20
Мне не нравится Decompose Conditional, потому что он загрязняет структуру кода одноразовыми функциями, которые нельзя использовать повторно. Я бы предпочел большое заявление IF с комментариями для каждой «группы» связанных проверок.
Milan Babuškov 9.05.2010 12:03:28

Здесь есть две проблемы: удобочитаемость и понятность

Решение «удобочитаемости» - это вопрос стиля, и поэтому оно открыто для интерпретации. Мое предпочтение таково:

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.

Это формально известно как шаблон рефакторинга «Условное разложение». Такие инструменты, как Resharper или Refactor Pro! может сделать процесс рефакторинга таким простым!

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

7
8.08.2008 17:13:40

Если вам случается программировать на 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?). Если так, то это, вероятно, самый чистый подход.

0
8.08.2008 17:11:23

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

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

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

-2
8.08.2008 17:13:49
Это, пожалуй, худшее решение этого вопроса.
Brad Gilbert 14.10.2008 20:59:45

Макдауэлл,

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

0
8.08.2008 17:29:52

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

1
8.08.2008 17:33:46

@tweakt

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

логическое ok = cond1; хорошо & = cond2; хорошо & = cond3; хорошо & = cond4; хорошо & = cond5; хорошо & = cond6;

Который так же, как:

ok = (cond1 && cond2 && cond3 && cond4 && cond5 && cond6);

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

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

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

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

0
8.08.2008 17:37:12

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

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 );
  }
};
2
14.10.2008 17:07:27
Приятно! Один, чтобы скачать и играть с Metinks.
toolkit 9.10.2008 15:07:23

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

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
}
0
13.05.2010 18:15:00
Какой смысл в создании отдельных bools. Кроме того , вы сравните друг varXс trueи присвоить это к isVarXValidBOOL, который является по существу только от руки для isVar1Valid = var, который является излишним. У вас уже есть bools для начала, так почему бы не простоif(var1 && var2 && var3 && var4)
dreamlax 13.05.2010 10:57:44

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

2
18.08.2008 05:39:28

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

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

  ... # Place Code Here
}

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

0
14.10.2008 21:07:38

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

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }
1
9.05.2010 12:01:29
    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)
       )
    {
    }

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

0
3.05.2017 17:11:48