嵌套冗余' if'条件

时间:2014-03-18 12:18:01

标签: c++ c if-statement

是否有更好(或更清洁)的方法来编写以下代码?

if(conditionX)
{
    if(condition1)
    {
        // code X1
    }
    else if(condition2)
    {
        // code X2
    }
}
else if(conditionY)
{
    if(condition1)
    {
        // code Y1
    }
    else if(condition2)
    {
        // code Y2
    }
}

我还有一些条件,但我想你明白了。

9 个答案:

答案 0 :(得分:34)

这个问题有四种方法,其中没有一种是通用的:

  1. 保持原样 - 此处没有太多代码重复。如果计算condition1condition2很棘手,请事先计算并将其存储在bool个变量中
  2. conditionXconditionY生成一个结果,让您统一condition1condition2 - 这并非总是可行,但在某些情况下您可以准备一个变量来统一两个分支中的活动,例如,使用函数指针或lambda。
  3. 将处理逻辑放入具有虚函数的子类中以消除条件逻辑 - 只有当您的初始设计错过了子类化的机会时才有可能。从本质上讲,这种方法将conditionX / conditionY的决定推送到创建子类的地方,然后通过调用接口中虚拟函数的正确覆盖来“重用”该决定。
  4. 创建表示所有三个条件的数字组合,然后转换为switch - 此技巧统一条件,减少嵌套。
  5. 以下是最后一种方法的示例:

    int caseNumber = ((conditionX?1:0) << 3)
                   | ((conditionY?1:0) << 2)
                   | ((condition2?1:0) << 1)
                   | ((condition1?1:0) << 0);
    switch (caseNumber) {
        case 0x09:
        case 0x0D:
        case 0x0F: // code X1
            break;
        case 0x0A:
        case 0x0E: // code X2
            break;
        case 0x05:
        case 0x07: // code Y1
            break;
        case 0x06: // code Y2
            break;
    }
    

答案 1 :(得分:25)

如果您在查看源代码时关注的是干净的代码,我的建议是将块分隔到各自的部分,例如:

if      (conditionX) processConditionX();
else if (conditionY) processConditionY();

等等。

然后,在子功能中,放置&#34;肉&#34;:

void processConditionX (void) {
    if(condition1) {
        // code X1
    } else if(condition2) {
        // code X2
    }
}

您可以修改它以传入并根据需要返回参数,并且我会使条件和函数名称更具描述性,但我认为它们只是这里的示例。

答案 2 :(得分:14)

您可以改为使用状态机:

#define COMBINATION(a,b,c,d) (((a)<<3)|((b)<<2)|((c)<<1)|((d)<<0))

switch (COMBINATION(conditionX,conditionY,condition1,condition2))
{
    case COMBINATION(0,0,0,0):           break;
    case COMBINATION(0,0,0,1):           break;
    case COMBINATION(0,0,1,0):           break;
    case COMBINATION(0,0,1,1):           break;
    case COMBINATION(0,1,0,0):           break;
    case COMBINATION(0,1,0,1): CodeY2(); break;
    case COMBINATION(0,1,1,0): CodeY1(); break;
    case COMBINATION(0,1,1,1): CodeY1(); break;
    case COMBINATION(1,0,0,0):           break;
    case COMBINATION(1,0,0,1): CodeX2(); break;
    case COMBINATION(1,0,1,0): CodeX1(); break;
    case COMBINATION(1,0,1,1): CodeX1(); break;
    case COMBINATION(1,1,0,0):           break;
    case COMBINATION(1,1,0,1): CodeX2(); break;
    case COMBINATION(1,1,1,0): CodeX1(); break;
    case COMBINATION(1,1,1,1): CodeX1(); break;
}

这只包含一个分支操作,因此它可能更高效(即使它还包括额外的运行时计算(在switch行))。

至于更清洁,我想这是一个透视问题,但上面的模板还为您提供了一种检测代码中所有未处理分支的便捷方法。

请注意,如果任何条件变量的值不是1或0,那么您应该:

#define COMBINATION(a,b,c,d) (((a)?8:0)|((b)?4:0)|((c)?2:0)|((d)?1:0))

更新(在以下评论之一中归因于@Jonathan Wakely):

如果您使用的是C ++ 11,则可以使用COMBINATION函数替换constexpr宏:

constexpr int COMBINATION(bool a,bool b,bool c,bool d)
{
    return ((int)a<<3) | ((int)b<<2) | ((int)c<<1) | ((int)d<<0);
}

答案 3 :(得分:6)

我会在第一个if中作为参数提供决策,然后分隔函数决定执行哪个代码,如:

if(conditionX)
{
    Method1(Condition Parameters)
}
else if(conditionY)
{
    Method1(Condition Parameters)
}

另一种方法是向决策方法(矩阵)提供所有需要的信息,此方法返回一个整数,您在switch语句中使用该整数来决定执行哪个代码。通过这种方式,您可以将决策逻辑分开,使其可读并且在需要时可以轻松进行单元测试:

DecisionMatrix(conditionX, conditionY, condition1, condition2)
{
  //  return a value according to the conditions for Example:
  // CoditionX + Condition1 => return 1
  // CoditionX + Condition2 => return 2
  // CoditionY + Condition1 => return 3
  // CoditionY + Condition2 => return 4
}

switch(DecisionMatrix)
{
    case 1: //run code X1       
    break;
    case 2: //run code X2
    break;
    case 3: //run code Y1       
    break;
    case 4: //run code Y2
    break;
}

答案 4 :(得分:4)

  1. 这里最好的方法是使用多态(仅当代码块很大时)

  2. 如果它们是小代码片段,那么创建类显然会有点过分。

    因此,如果所有代码都有相似之处,我建议看似简单但非常困难的任务。

    • 尝试尽可能多地对它们进行参数化。
    • 创建一个能够接受这些并在条件中调用它们的函数
    • 现在代码将出现在功能块和&#34; cleaner&#34;
  3. 创造简单的东西总是很困难。

    if (conditionX) {
        method(parameterX);
    else if (conditionY) {
        method(parameterY);
    }
    

    ,其中

    void method(ParameterType e) {
        if (condition 1) {
            // Code in terms of parameter e
        } else if (condition2) {
            // Code in terms of parameter e
        }
    }
    

    您可以将参数化的条件保留在外面。

    希望这有帮助。

答案 5 :(得分:3)

我认为这种方式可以解决您的代码。

enum ConditionParentType
{
    CONDITION_NONE = 0,
    CONDITION_X,
    CONDITION_Y,
};

enum ConditionChildType
{
    CONDITION_0 = 0,
    CONDITION_1,
    CONDITION_2,
};

class ConditionHandler
{
public:
    explicit ConditionHandler(ConditionParentType p_type, ConditionChildType c_type) 
        : p_type_(p_type), c_type_(c_type) {};
    void DoAction()
    {
        if(child_type == CONDITION_1)
        {

        }
        else if(child_type == CONDITION_2)
        {

        }
        else
        {
            //error
        }
    }

private:
    const ConditionParentType p_type_;
    const ConditionChildType  c_type_;
}; 

int main(int argc, char *argv[]) 
{
    ConditionParentType parent_type = GetParentType();
    ConditionChildType  child_type  = GetChildType();

    ConditionHandler handler(parent_type, child_type);
    handler.DoAction();

    getchar();

    return 0;
}

答案 6 :(得分:3)

如果条件的组合意味着什么,那么我会编写一组返回布尔值的简单方法。你最终会得到类似的东西:

  if (first-condition(conditionX, condition1)) {
    // code X1
  } else if (first-condition(conditionX, condition2)) {
    // code X2
  } else if (third-condition(conditionY, condition1)) {
    // code Y1
  } else if (fourth-condition(conditionY, condition2)) {
    // code Y2
  }

方法的名称描述了条件。不要担心这些方法只被调用一次(编译器可能无论如何都会内联它们),重要的一点是你的代码然后变成自我记录。

答案 7 :(得分:3)

我对其他建议的答案感到非常惊讶,如果出现以下情况,这些答案大多是错误的:

  • 两个重复的条件condition1condition2很复杂,在这种情况下DRY开始发挥作用,或者
  • 四种情况中的任何一种都有副作用,或
  • 任何条件都很慢(例如,找到大型数组的最小值,或读取文件),或
  • 需要布尔短路,如:if (p == 0) {...} else if (p->foo == 42) {...}

如果这些都没有,99.42%的情况都是如此,那么保持代码原样。或者,作为一个小变化,改变它,使嵌套(即缩进)只有一个级别,而不是两个级别。

否则,您将需要使用临时变量,如下所示

const bool tstX =         (conditionX);    
const bool tstY = tstX || (conditionY);    
const bool tst1 = tstY && (condition1);
const bool tst2 = tstY && !tst1 && (condition2);

答案 8 :(得分:1)

原始代码看起来并不坏。根据具体情况,可能会或可能不会更具可读性:

if(conditionX and condition1) {
  // code X1
} 
else if(conditionX and condition2) {
  // code X2
}
else if(conditionY and condition1) {
  // code Y1
}
else if(conditionY and condition2)
  // code Y2
}