ABC*_*lus 262 c c++ if-statement control-flow
假设我有这个伪代码:
bool conditionA = executeStepA();
if (conditionA){
bool conditionB = executeStepB();
if (conditionB){
bool conditionC = executeStepC();
if (conditionC){
...
}
}
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
executeStepX当且仅当前一个成功时才应执行函数.无论如何,executeThisFunctionInAnyCase应该在最后调用该函数.我是编程的新手,对于这个非常基本的问题感到抱歉:有没有办法(例如在C/C++中)避免长if链产生那种"代码金字塔",代价是代码易读性?
我知道如果我们可以跳过executeThisFunctionInAnyCase函数调用,代码可以简化为:
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
Run Code Online (Sandbox Code Playgroud)
但约束是executeThisFunctionInAnyCase函数调用.该break声明是否可以某种方式使用?
Sho*_*hoe 485
你可以使用&&(逻辑AND):
if (executeStepA() && executeStepB() && executeStepC()){
...
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
这将满足您的两个要求:
executeStep<X>()应仅评估前一个是否成功(这称为短路评估)executeThisFunctionInAnyCase() 将在任何情况下执行ltj*_*jax 358
只需使用其他功能即可使您的第二个版本正常工作:
void foo()
{
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
}
void bar()
{
foo();
executeThisFunctionInAnyCase();
}
Run Code Online (Sandbox Code Playgroud)
使用深度嵌套的ifs(你的第一个变体)或者想要打破"函数的一部分"通常意味着你需要一个额外的功能.
cma*_*ter 165
老派C程序员goto在这种情况下使用.这是gotoLinux风格指南实际鼓励的一种用法,它被称为集中式功能退出:
int foo() {
int result = /*some error code*/;
if(!executeStepA()) goto cleanup;
if(!executeStepB()) goto cleanup;
if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
executeThisFunctionInAnyCase();
return result;
}
Run Code Online (Sandbox Code Playgroud)
有些人goto通过将身体包裹成一个环并打破它来解决使用问题,但实际上两种方法都做同样的事情.goto如果只有executeStepA()成功的话需要进行其他清理,方法会更好:
int foo() {
int result = /*some error code*/;
if(!executeStepA()) goto cleanupPart;
if(!executeStepB()) goto cleanup;
if(!executeStepC()) goto cleanup;
result = 0;
cleanup:
innerCleanup();
cleanupPart:
executeThisFunctionInAnyCase();
return result;
}
Run Code Online (Sandbox Code Playgroud)
使用循环方法,在这种情况下最终会有两级循环.
Joh*_* Wu 129
这是一种常见的情况,有许多常见的方法可以解决它.这是我对规范答案的尝试.如果我遗漏了任何内容,请发表评论,我会及时更新这篇文章.
你在讨论什么被称为箭头反模式.它被称为箭头,因为嵌套ifs链形成代码块,它们向右和向后扩展得越来越远,形成一个"指向"代码编辑器窗格右侧的可视箭头.
这里讨论一些避免箭头的常见方法.最常见的方法是使用保护模式,其中代码首先处理异常流,然后处理基本流,例如,而不是
if (ok)
{
DoSomething();
}
else
{
_log.Error("oops");
return;
}
Run Code Online (Sandbox Code Playgroud)
......你用......
if (!ok)
{
_log.Error("oops");
return;
}
DoSomething(); //notice how this is already farther to the left than the example above
Run Code Online (Sandbox Code Playgroud)
当有一长串防护装置时,这会使代码变得相当扁平,因为所有防护装置一直显示在左边,而你的ifs没有嵌套.此外,您可以直观地将逻辑条件与其相关错误配对,这样可以更容易地判断出发生了什么:
箭头:
ok = DoSomething1();
if (ok)
{
ok = DoSomething2();
if (ok)
{
ok = DoSomething3();
if (!ok)
{
_log.Error("oops"); //Tip of the Arrow
return;
}
}
else
{
_log.Error("oops");
return;
}
}
else
{
_log.Error("oops");
return;
}
Run Code Online (Sandbox Code Playgroud)
守护:
ok = DoSomething1();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething2();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething3();
if (!ok)
{
_log.Error("oops");
return;
}
ok = DoSomething4();
if (!ok)
{
_log.Error("oops");
return;
}
Run Code Online (Sandbox Code Playgroud)
这是客观和量化的更容易阅读,因为
守卫模式的问题在于它依赖于所谓的"机会主义回归"或"机会主义退出".换句话说,它打破了每个函数应该只有一个退出点的模式.这是一个问题有两个原因:
下面我提供了一些通过使用语言功能或完全避免这个问题来解决这个限制的选项.
finally不幸的是,作为一名c ++开发人员,你无法做到这一点.但这是包含finally关键字的语言的头号答案,因为这正是它的用途.
try
{
if (!ok)
{
_log.Error("oops");
return;
}
DoSomething(); //notice how this is already farther to the left than the example above
}
finally
{
DoSomethingNoMatterWhat();
}
Run Code Online (Sandbox Code Playgroud)
您可以通过将代码分解为两个函数来避免此问题.该解决方案具有适用于任何语言的优势,此外它还可以降低圈复杂度,这是降低缺陷率的有效方法,可提高任何自动化单元测试的特异性.
这是一个例子:
void OuterFunction()
{
DoSomethingIfPossible();
DoSomethingNoMatterWhat();
}
void DoSomethingIfPossible()
{
if (!ok)
{
_log.Error("Oops");
return;
}
DoSomething();
}
Run Code Online (Sandbox Code Playgroud)
我看到的另一个常见技巧是使用while(true)和break,如其他答案所示.
while(true)
{
if (!ok) break;
DoSomething();
break; //important
}
DoSomethingNoMatterWhat();
Run Code Online (Sandbox Code Playgroud)
虽然这不像使用那么"诚实" goto,但它在重构时不太容易被搞砸,因为它清楚地标明了逻辑范围的界限.切割和粘贴标签或goto声明的天真编码器可能会导致严重问题!(坦率地说,这种模式是如此常见,现在我认为它清楚地传达了意图,因此根本不是"不诚实").
此选项还有其他变体.例如,可以使用switch而不是while.任何带有break关键字的语言构造都可能有效.
另一种方法利用对象生命周期.使用上下文对象来携带你的参数(这是我们天真的例子可疑的缺点)并在你完成后处理它.
class MyContext
{
~MyContext()
{
DoSomethingNoMatterWhat();
}
}
void MainMethod()
{
MyContext myContext;
ok = DoSomething(myContext);
if (!ok)
{
_log.Error("Oops");
return;
}
ok = DoSomethingElse(myContext);
if (!ok)
{
_log.Error("Oops");
return;
}
ok = DoSomethingMore(myContext);
if (!ok)
{
_log.Error("Oops");
}
//DoSomethingNoMatterWhat will be called when myContext goes out of scope
}
Run Code Online (Sandbox Code Playgroud)
注意:确保您了解所选语言的对象生命周期.为此需要某种确定性的垃圾收集,即你必须知道何时调用析构函数.在某些语言中,您将需要使用Dispose而不是析构函数.
如果您打算使用面向对象的方法,也可以正确使用.此选项使用类来"包装"需要清理的资源以及其他操作.
class MyWrapper
{
bool DoSomething() {...};
bool DoSomethingElse() {...}
void ~MyWapper()
{
DoSomethingNoMatterWhat();
}
}
void MainMethod()
{
bool ok = myWrapper.DoSomething();
if (!ok)
_log.Error("Oops");
return;
}
ok = myWrapper.DoSomethingElse();
if (!ok)
_log.Error("Oops");
return;
}
}
//DoSomethingNoMatterWhat will be called when myWrapper is destroyed
Run Code Online (Sandbox Code Playgroud)
再次,确保您了解对象的生命周期.
另一种技术是利用短路评估.
if (DoSomething1() && DoSomething2() && DoSomething3())
{
DoSomething4();
}
DoSomethingNoMatterWhat();
Run Code Online (Sandbox Code Playgroud)
此解决方案利用了&&运算符的工作方式.当&&的左侧评估为false时,永远不会评估右侧.
当需要紧凑代码并且代码不太可能看到很多维护时,这个技巧最有用,例如,您正在实现一个众所周知的算法.对于更一般的编码,此代码的结构太脆弱; 即使是对逻辑的微小改动也可能触发完全重写.
Che*_*Alf 60
做就是了
if( executeStepA() && executeStepB() && executeStepC() )
{
// ...
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
就这么简单.
由于三个编辑每个都从根本上改变了问题(如果将修订版本重新计算为版本#1,则有四个),我将包含我正在回答的代码示例:
bool conditionA = executeStepA();
if (conditionA){
bool conditionB = executeStepB();
if (conditionB){
bool conditionC = executeStepC();
if (conditionC){
...
}
}
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
Mat*_* M. 35
实际上有一种方法可以推迟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
Run Code Online (Sandbox Code Playgroud)
然后使用该实用程序:
int foo() {
Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda
// ...
if (!executeA()) { return 1; }
// ...
if (!executeB()) { return 2; }
// ...
if (!executeC()) { return 3; }
// ...
return 4;
} // foo
Run Code Online (Sandbox Code Playgroud)
Deb*_*sis 34
有一个很好的技术,不需要使用return语句(Itjax规定的方法)的附加包装函数.它使用do while(0)伪循环.在while (0)确保它其实并不是一个循环,但只执行一次.但是,循环语法允许使用break语句.
void foo()
{
// ...
do {
if (!executeStepA())
break;
if (!executeStepB())
break;
if (!executeStepC())
break;
}
while (0);
// ...
}
Run Code Online (Sandbox Code Playgroud)
小智 19
你也可以这样做:
bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr
funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...
//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it)
isOk = (*it)();
if (isOk)
//doSomeStuff
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以获得最小的线性增长大小,每次调用+1行,并且可以轻松维护.
编辑 :(谢谢@Unda)不是一个大粉丝,因为你失去了可见性IMO:
bool isOk = true;
auto funcs { //using c++11 initializer_list
&executeStepA,
&executeStepB,
&executeStepC
};
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it)
isOk = (*it)();
if (isOk)
//doSomeStuff
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
sam*_*ris 18
这会有用吗?我认为这与您的代码相同.
bool condition = true; // using only one boolean variable
if (condition) condition = executeStepA();
if (condition) condition = executeStepB();
if (condition) condition = executeStepC();
...
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
Cli*_*ick 14
假设所需的代码是我目前看到的:
bool conditionA = executeStepA();
if (conditionA){
bool conditionB = executeStepB();
if (conditionB){
bool conditionC = executeStepC();
if (conditionC){
...
}
}
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
我会说正确的方法,因为它是最简单的阅读和最容易维护,会有较少的缩进程度,这是(目前)问题的既定目的.
// Pre-declare the variables for the conditions
bool conditionA = false;
bool conditionB = false;
bool conditionC = false;
// Execute each step only if the pre-conditions are met
conditionA = executeStepA();
if (conditionA)
conditionB = executeStepB();
if (conditionB)
conditionC = executeStepC();
if (conditionC) {
...
}
// Unconditionally execute the 'cleanup' part.
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
这避免了对s,异常,虚拟循环或其他困难构造的任何需要,并且只需简单地完成手头的简单工作.gotowhile
oua*_*uah 12
可以用某种方式使用break语句吗?
也许不是最好的解决方案,但你可以将你的语句放在一个do .. while (0)循环中,break而不是使用语句 return.
Nia*_*all 12
您可以将所有if条件(根据需要进行格式化)放在自己的函数中,on返回执行executeThisFunctionInAnyCase()函数.
从OP中的基本示例,条件测试和执行可以这样拆分;
void InitialSteps()
{
bool conditionA = executeStepA();
if (!conditionA)
return;
bool conditionB = executeStepB();
if (!conditionB)
return;
bool conditionC = executeStepC();
if (!conditionC)
return;
}
Run Code Online (Sandbox Code Playgroud)
然后这样称呼;
InitialSteps();
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
如果C++ 11 lambdas可用(OP中没有C++ 11标签,但它们可能仍然是一个选项),那么我们可以放弃单独的函数并将其包装成lambda.
// Capture by reference (variable access may be required)
auto initialSteps = [&]() {
// any additional code
bool conditionA = executeStepA();
if (!conditionA)
return;
// any additional code
bool conditionB = executeStepB();
if (!conditionB)
return;
// any additional code
bool conditionC = executeStepC();
if (!conditionC)
return;
};
initialSteps();
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
Ale*_*lex 10
如果您不喜欢goto和不喜欢do { } while (0);循环并喜欢使用C++,您也可以使用临时lambda来产生相同的效果.
[&]() { // create a capture all lambda
if (!executeStepA()) { return; }
if (!executeStepB()) { return; }
if (!executeStepC()) { return; }
}(); // and immediately call it
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
代码中的IF/ELSE链不是语言问题,而是程序的设计.如果您能够重新计算或重写您的程序,我建议您查看设计模式(http://sourcemaking.com/design_patterns)以找到更好的解决方案.
通常,当您在代码中看到很多IF和其他内容时,可以实现策略设计模式(http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net)或者组合其他模式.
我确定有一些替代方法可以写一个if/else的长列表,但我怀疑他们会改变任何东西,除了链对你来说很漂亮(但是,旁观者眼中的美仍然适用于代码也是:-)).您应该关注这样的事情(在6个月内,当我遇到新情况并且我不记得有关此代码的任何内容时,我能够轻松添加它吗?或者如果链条发生变化,速度和无错误怎么办?我会实施吗)
你这样做..
coverConditions();
executeThisFunctionInAnyCase();
function coverConditions()
{
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;
}
Run Code Online (Sandbox Code Playgroud)
99次100,这是唯一的方法.
永远不要试图在计算机代码中做一些"棘手"的事情.
顺便说一句,我很确定以下是你想到的实际解决方案......
该继续语句是算法编程的关键.(就像goto语句在算法编程中至关重要.)
在许多编程语言中,您可以这样做:
-(void)_testKode
{
NSLog(@"code a");
NSLog(@"code b");
NSLog(@"code c\n");
int x = 69;
{
if ( x == 13 )
{
NSLog(@"code d---\n");
continue;
}
if ( x == 69 )
{
NSLog(@"code e---\n");
continue;
}
if ( x == 13 )
{
NSLog(@"code f---\n");
continue;
}
}
NSLog(@"code g");
}
Run Code Online (Sandbox Code Playgroud)
(首先请注意:像这样的裸块是编写漂亮代码的关键和重要部分,特别是如果你正在处理"算法"编程.)
再一次,这正是你脑子里的那个,对吗?这是写它的美妙方式,所以你有很好的直觉.
然而,不幸的是,在当前版本的objective-c(旁白 - 我不知道Swift,对不起),有一个可行的功能,它检查封闭块是否是一个循环.

-(void)_testKode
{
NSLog(@"code a");
NSLog(@"code b");
NSLog(@"code c\n");
int x = 69;
do{
if ( x == 13 )
{
NSLog(@"code d---\n");
continue;
}
if ( x == 69 )
{
NSLog(@"code e---\n");
continue;
}
if ( x == 13 )
{
NSLog(@"code f---\n");
continue;
}
}while(false);
NSLog(@"code g");
}
Run Code Online (Sandbox Code Playgroud)
所以不要忘记..
只是意味着"做一次阻止".
也就是说,写作do{}while(false);和简单写作完全没有区别{}.
这现在可以完美地按照您的要求运行......这是输出......

所以,你可能会在脑海中看到算法.你应该总是试着写下你脑子里的东西.(特别是如果你不清醒,因为那是漂亮的时候!:))
在"算法"项目中,这种情况发生了很多,在objective-c中,我们总是有一个宏...
#define RUNONCE while(false)
Run Code Online (Sandbox Code Playgroud)
......那么你可以这样做......
-(void)_testKode
{
NSLog(@"code a");
int x = 69;
do{
if ( x == 13 )
{
NSLog(@"code d---\n");
continue;
}
if ( x == 69 )
{
NSLog(@"code e---\n");
continue;
}
if ( x == 13 )
{
NSLog(@"code f---\n");
continue;
}
}RUNONCE
NSLog(@"code g");
}
Run Code Online (Sandbox Code Playgroud)
有两点:
a,尽管目标-c检查一个继续语句所在的块的类型是愚蠢的,但是"打那个"很麻烦.所以这是一个艰难的决定.
b,在这个例子中,你应该缩进那个块吗?我在这样的问题上失眠了,所以我无法提出建议.
希望能帮助到你.
如果执行函数失败而不是返回false,则抛出异常.那么你的调用代码可能如下所示:
try {
executeStepA();
executeStepB();
executeStepC();
}
catch (...)
Run Code Online (Sandbox Code Playgroud)
当然我假设在你的原始例子中,执行步骤只会在步骤内发生错误的情况下返回false?
已经有很多好的答案,但是他们中的大多数人似乎都在一定程度上(实际上很少)的灵活性进行权衡.不需要这种权衡的常见方法是添加状态/保持变量.当然,价格是追踪的一个额外价值:
bool ok = true;
bool conditionA = executeStepA();
// ... possibly edit conditionA, or just ok &= executeStepA();
ok &= conditionA;
if (ok) {
bool conditionB = executeStepB();
// ... possibly do more stuff
ok &= conditionB;
}
if (ok) {
bool conditionC = executeStepC();
ok &= conditionC;
}
if (ok && additionalCondition) {
// ...
}
executeThisFunctionInAnyCase();
// can now also:
return ok;
Run Code Online (Sandbox Code Playgroud)
在C++中(问题被标记为C和C++),如果你不能改变函数来使用异常,你仍然可以使用异常机制,如果你写一个小辅助函数,如
struct function_failed {};
void attempt(bool retval)
{
if (!retval)
throw function_failed(); // or a more specific exception class
}
Run Code Online (Sandbox Code Playgroud)
然后您的代码可以如下所示:
try
{
attempt(executeStepA());
attempt(executeStepB());
attempt(executeStepC());
}
catch (function_failed)
{
// -- this block intentionally left empty --
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
如果你喜欢花哨的语法,你可以通过显式转换使它工作:
struct function_failed {};
struct attempt
{
attempt(bool retval)
{
if (!retval)
throw function_failed();
}
};
Run Code Online (Sandbox Code Playgroud)
然后你可以编写你的代码
try
{
(attempt) executeStepA();
(attempt) executeStepB();
(attempt) executeStepC();
}
catch (function_failed)
{
// -- this block intentionally left empty --
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
对于C++ 11及更高版本,一个很好的方法可能是实现类似于D的范围(退出)机制的范围退出系统.
实现它的一种可能方法是使用C++ 11 lambda和一些辅助宏:
template<typename F> struct ScopeExit
{
ScopeExit(F f) : fn(f) { }
~ScopeExit()
{
fn();
}
F fn;
};
template<typename F> ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); };
#define STR_APPEND2_HELPER(x, y) x##y
#define STR_APPEND2(x, y) STR_APPEND2_HELPER(x, y)
#define SCOPE_EXIT(code)\
auto STR_APPEND2(scope_exit_, __LINE__) = MakeScopeExit([&](){ code })
Run Code Online (Sandbox Code Playgroud)
这将允许您从函数中提前返回并确保您定义的任何清理代码始终在作用域退出时执行:
SCOPE_EXIT(
delete pointerA;
delete pointerB;
close(fileC); );
if (!executeStepA())
return;
if (!executeStepB())
return;
if (!executeStepC())
return;
Run Code Online (Sandbox Code Playgroud)
这些宏真的只是装饰.MakeScopeExit()可以直接使用.
如果您的代码与示例一样简单,并且您的语言支持短路评估,那么您可以尝试这样做:
StepA() && StepB() && StepC() && StepD();
DoAlways();
Run Code Online (Sandbox Code Playgroud)
如果您将参数传递给函数并返回其他结果,以便您的代码无法以先前的方式编写,那么许多其他答案将更适合于该问题.
为什么没有人提供最简单的解决方案?:d
如果您的所有函数都具有相同的签名,那么您可以这样做(对于C语言):
bool (*step[])() = {
&executeStepA,
&executeStepB,
&executeStepC,
...
};
for (int i = 0; i < numberOfSteps; i++) {
bool condition = step[i]();
if (!condition) {
break;
}
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
对于干净的C++解决方案,您应该创建一个包含execute方法的接口类, 并将您的步骤包装在对象中.
然后,上面的解决方案将如下所示:
Step *steps[] = {
stepA,
stepB,
stepC,
...
};
for (int i = 0; i < numberOfSteps; i++) {
Step *step = steps[i];
if (!step->execute()) {
break;
}
}
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
几个答案暗示了我多次看到和使用的模式,尤其是在网络编程中。在网络堆栈中,通常存在一长串请求,其中任何一个请求都可能失败并停止进程。
常见的模式是使用do { } while (false);
我使用了一个宏来while(false)实现它,do { } once;常见的模式是:
do
{
bool conditionA = executeStepA();
if (! conditionA) break;
bool conditionB = executeStepB();
if (! conditionB) break;
// etc.
} while (false);
Run Code Online (Sandbox Code Playgroud)
这种模式相对容易阅读,并且允许使用能够正确破坏的对象,并且还避免了多次返回,从而使单步执行和调试变得更加容易。
假设您不需要单独的条件变量,反转测试并使用else-falthrough作为"ok"路径将允许您获得更多垂直的if/else语句集:
bool failed = false;
// keep going if we don't fail
if (failed = !executeStepA()) {}
else if (failed = !executeStepB()) {}
else if (failed = !executeStepC()) {}
else if (failed = !executeStepD()) {}
runThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
省略失败的变量会使代码过于模糊IMO.
在里面声明变量很好,不用担心= vs ==.
// keep going if we don't fail
if (bool failA = !executeStepA()) {}
else if (bool failB = !executeStepB()) {}
else if (bool failC = !executeStepC()) {}
else if (bool failD = !executeStepD()) {}
else {
// success !
}
runThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
这是模糊的,但紧凑:
// keep going if we don't fail
if (!executeStepA()) {}
else if (!executeStepB()) {}
else if (!executeStepC()) {}
else if (!executeStepD()) {}
else { /* success */ }
runThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)
正如Rommik所提到的,你可以为此应用一个设计模式,但我会使用Decorator模式而不是策略,因为你想要链接调用.如果代码很简单,那么我会使用一个结构良好的答案来防止嵌套.但是,如果它很复杂或需要动态链接,那么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;
}
}
Run Code Online (Sandbox Code Playgroud)
关于设计模式的最佳书籍,恕我直言,是Head First Design Patterns.
这看起来像一个状态机,这很方便,因为您可以使用状态模式轻松实现它.
在Java中,它看起来像这样:
interface StepState{
public StepState performStep();
}
Run Code Online (Sandbox Code Playgroud)
实施工作如下:
class StepA implements StepState{
public StepState performStep()
{
performAction();
if(condition) return new StepB()
else return null;
}
}
Run Code Online (Sandbox Code Playgroud)
等等.然后你可以用以下方法替换大if条件:
Step toDo = new StepA();
while(toDo != null)
toDo = toDo.performStep();
executeThisFunctionInAnyCase();
Run Code Online (Sandbox Code Playgroud)