Подтвердить что ты не робот

Как вы справляетесь с огромными 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)
     {
4b9b3361

Ответ 1

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

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

Удостоверьтесь, что вы указали имя переменных, которые на самом деле указывают на намерение, а не на функцию. Это очень поможет разработчику, поддерживающему ваш код... он может быть ВАМИ!

Ответ 3

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

Решение "читаемости" является проблемой стиля и, следовательно, открыто для интерпретации. Мое предпочтение таково:

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" и т.д. Являются не допустимыми именами переменных. Они должны иметь имя, которое отражает основной характер данных, которые они представляют.

Ответ 4

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

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

Ответ 5

Сначала я удалю все части == true, что сделает его на 50% короче;)

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

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

Ответ 6

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

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

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
}

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

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

Ответ 7

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

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

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

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

Но я не думаю, что это довольно чисто.

Ответ 8

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

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

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

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

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

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

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

Ответ 9

Попробуйте взглянуть на Функторы и Предикаты. Проект 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 );

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

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

Ответ 10

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

Ответ 11

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

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

if ( cond1 && cond2 ) {}

Ответ 12

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

Ответ 13

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

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($$v == true) {
        // do something
        break;
    }

Ответ 14

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

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)){

Ответ 15

Если вы планируете программировать на Python, это будет cinch со встроенной функцией 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

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

Ответ 16

Макдауэлл,

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

Ответ 17

@tweakt

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

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

Это то же самое, что:

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

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

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

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

while (false) является своего рода дрянным. Я бы хотел, чтобы на языках появился оператор под названием "один раз" или что-то, что вы могли бы легко вырваться.

Ответ 18

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

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
}

Ответ 19

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

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

  ... # Place Code Here
}

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

Ответ 20

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

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

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

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

Итак,...

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

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

Ответ 21

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

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

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