假设我有这样的伪代码:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

函数executeStepX当且仅当前一个成功时执行。 在任何情况下,executeThisFunctionInAnyCase函数都应该在最后被调用。 我在编程方面是一个新手,所以很抱歉提出一个非常基本的问题:有没有一种方法(例如在C/ c++中)以代码易读性为代价,避免长if链产生那种“金字塔式代码”?

我知道如果我们可以跳过executeThisFunctionInAnyCase函数调用,代码可以简化为:

bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

但是约束是executeThisFunctionInAnyCase函数调用。 break语句可以以某种方式使用吗?


当前回答

正如Rommik提到的,您可以为此应用设计模式,但我将使用Decorator模式而不是Strategy,因为您想要链式调用。如果代码很简单,那么我会选择一个结构良好的答案来防止嵌套。但是,如果它很复杂或者需要动态链接,那么Decorator模式是一个很好的选择。这是一个yUML类图:

下面是一个示例LinqPad c#程序:

void Main()
{
    IOperation step = new StepC();
    step = new StepB(step);
    step = new StepA(step);
    step.Next();
}

public interface IOperation 
{
    bool Next();
}

public class StepA : IOperation
{
    private IOperation _chain;
    public StepA(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step A success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepB : IOperation
{
    private IOperation _chain;
    public StepB(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {   
        bool localResult = false;

        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to false, 
            // to show breaking out of the chain
        localResult = false;
        Console.WriteLine("Step B success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepC : IOperation
{
    private IOperation _chain;
    public StepC(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step C success={0}", localResult);
        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

恕我直言,关于设计模式最好的书是《Head First design patterns》。

其他回答

把有条件的东西移到else中怎么样?

if (!(conditionA = executeStepA()){}
else if (!(conditionB = executeStepB()){}
else if (!(conditionC = executeStepC()){}
else if (!(conditionD = executeStepD()){}

这确实解决了缩进问题。

As @Jefffrey said, you can use the conditional short-circuit feature in almost every language, I personally dislike conditional statements with more than 2 condition (more than a single && or ||), just a matter of style. This code does the same (and probably would compile the same) and it looks a bit cleaner to me. You don't need curly braces, breaks, returns, functions, lambdas (only c++11), objects, etc. as long as every function in executeStepX() returns a value that can be cast to true if the next statement is to be executed or false otherwise.

if (executeStepA())
 if (executeStepB())
  if (executeStepC())
   //...
    if (executeStepN()); // <-- note the ';'

executeThisFunctionInAnyCase();

任何时候,任何函数返回false,都不会调用下一个函数。

我喜欢@Mayerz的答案,因为你可以在运行时改变要调用的函数(以及它们的顺序)。这有点像观察者模式,其中有一组订阅者(函数、对象等),只要满足给定的任意条件,就会调用和执行这些订阅者。在许多情况下,这可能是一个过度杀戮,所以明智地使用它:)

while(executeStepA() && executeStepB() && executeStepC() && 0);
executeThisFunctionInAnyCase();

executeThisFunctionInAnyCase()必须在任何情况下执行,即使其他函数没有完成。

while语句:

while(executeStepA() && executeStepB() && executeStepC() && 0)

将执行所有的函数,不会循环,因为这是一个明确的错误语句。 也可以在退出前重试一定次数。

一个简单的解决方案是使用一个条件布尔变量,同一个变量可以重复使用,以检查顺序执行的所有步骤的结果:

    bool cond = executeStepA();
    if(cond) cond = executeStepB();
    if(cond) cond = executeStepC();
    if(cond) cond = executeStepD();

    executeThisFunctionInAnyCase();

并不是说在此之前没有必要这样做:bool cond = true;... 然后后跟if(cond) cond = executeStepA();cond变量可以立即赋值给executeStepA()的结果,因此使代码更短,更易于阅读。

另一个更奇特但有趣的方法是这样的(有些人可能认为这是IOCCC的一个很好的候选,但仍然如此):

    !executeStepA() ? 0 :
      !executeStepB() ? 0 :
      !executeStepC() ? 0 :
      !executeStepD() ? 0 : 1 ;

    executeThisFunctionInAnyCase();

结果是完全相同的,如果我们做什么OP张贴,即:

    if(executeStepA()){
        if(executeStepB()){
            if(executeStepC()){
                if(executeStepD()){
                }
            }
        }
    }

    executeThisFunctionInAnyCase();

让执行函数在失败时抛出异常,而不是返回false。然后你的调用代码看起来像这样:

try {
    executeStepA();
    executeStepB();
    executeStepC();
}
catch (...)

当然,我假设在您最初的示例中,执行步骤只会在步骤内发生错误的情况下返回false ?