假设我有这样的伪代码:

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》。

其他回答

只做

if( executeStepA() && executeStepB() && executeStepC() )
{
    // ...
}
executeThisFunctionInAnyCase();

就是这么简单。


由于三次编辑都从根本上改变了问题(如果算上版本1的修订,则是四次),我包括了我正在回答的代码示例:

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

executeThisFunctionInAnyCase();

为了改进Mathieu的c++ 11答案并避免通过使用std::function而产生的运行时成本,我建议使用以下方法

template<typename functor>
class deferred final
{
public:
    template<typename functor2>
    explicit deferred(functor2&& f) : f(std::forward<functor2>(f)) {}
    ~deferred() { this->f(); }

private:
    functor f;
};

template<typename functor>
auto defer(functor&& f) -> deferred<typename std::decay<functor>::type>
{
    return deferred<typename std::decay<functor>::type>(std::forward<functor>(f));
}

这个简单的模板类将接受任何不需要任何参数就可以调用的函子,并且这样做不需要任何动态内存分配,因此更好地符合c++的抽象目标,没有不必要的开销。附加的函数模板用于简化模板参数推断的使用(类模板参数不可用)

使用的例子:

auto guard = defer(executeThisFunctionInAnyCase);
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

正如Mathieu的答案,这个解决方案是完全异常安全的,在所有情况下都将调用executeThisFunctionInAnyCase。如果executeThisFunctionInAnyCase本身被抛出,析构函数被隐式地标记为noexcept,因此将发出对std::terminate的调用,而不是在堆栈展开期间引发异常。

正如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》。

假循环已经提到了,但我没有看到下面的技巧在给出的答案:你可以使用一个do{/*…*/} while(evalates_to_zero ());实现一个双向早出中断。使用break终止循环而不计算条件语句,而使用continue将计算条件语句。

你可以使用它,如果你有两种类型的终结,其中一条路径必须比另一条路径做更多的工作:

#include <stdio.h>
#include <ctype.h>

int finalize(char ch)
{
    fprintf(stdout, "read a character: %c\n", (char)toupper(ch));

    return 0;
}

int main(int argc, char *argv[])
{
    int ch;
    do {
        ch = fgetc(stdin);
        if( isdigit(ch) ) {
            fprintf(stderr, "read a digit (%c): aborting!\n", (char)ch);
            break;
        }
        if( isalpha(ch) ) {
            continue;
        }
        fprintf(stdout, "thank you\n");
    } while( finalize(ch) );

    return 0;
}

执行此命令会得到以下会话协议:

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
-
thank you
read a character: -

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
a
read a character: A

dw@narfi ~/misc/test/fakeloopbreak $ ./fakeloopbreak 
1
read a digit (1): aborting!

在c++中,实际上有一种方法可以延迟操作:使用对象的析构函数。

假设你可以访问c++ 11:

class Defer {
public:
    Defer(std::function<void()> f): f_(std::move(f)) {}
    ~Defer() { if (f_) { f_(); } }

    void cancel() { f_ = std::function<void()>(); }

private:
    Defer(Defer const&) = delete;
    Defer& operator=(Defer const&) = delete;

    std::function<void()> f_;
}; // class Defer

然后使用这个工具:

int foo() {
    Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda

    // ...

    if (!executeA()) { return 1; }

    // ...

    if (!executeB()) { return 2; }

    // ...

    if (!executeC()) { return 3; }

    // ...

    return 4;
} // foo