假设我有这样的伪代码:
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语句可以以某种方式使用吗?
为什么没有人给出最简单的解决方案?: D
如果你所有的函数都有相同的签名,那么你可以这样做(对于C语言):
bool (*step[])() = {
&executeStepA,
&executeStepB,
&executeStepC,
...
};
for (int i = 0; i < numberOfSteps; i++) {
bool condition = step[i]();
if (!condition) {
break;
}
}
executeThisFunctionInAnyCase();
对于简洁的c++解决方案,您应该创建一个接口类,其中包含一个执行方法,并将步骤包装在对象中。
然后,上面的解看起来像这样:
Step *steps[] = {
stepA,
stepB,
stepC,
...
};
for (int i = 0; i < numberOfSteps; i++) {
Step *step = steps[i];
if (!step->execute()) {
break;
}
}
executeThisFunctionInAnyCase();
如果条件被移动到单独的步骤下,条件可以被简化,这是一个c#伪代码,
其思想是使用编排而不是中央编排。
void Main()
{
Request request = new Request();
Response response = null;
// enlist all the processors
var processors = new List<IProcessor>() {new StepA() };
var factory = new ProcessorFactory(processors);
// execute as a choreography rather as a central orchestration.
var processor = factory.Get(request, response);
while (processor != null)
{
processor.Handle(request, out response);
processor = factory.Get(request, response);
}
// final result...
//response
}
public class Request
{
}
public class Response
{
}
public interface IProcessor
{
bool CanProcess(Request request, Response response);
bool Handle(Request request, out Response response);
}
public interface IProcessorFactory
{
IProcessor Get(Request request, Response response);
}
public class ProcessorFactory : IProcessorFactory
{
private readonly IEnumerable<IProcessor> processors;
public ProcessorFactory(IEnumerable<IProcessor> processors)
{
this.processors = processors;
}
public IProcessor Get(Request request, Response response)
{
// this is an iterator
var matchingProcessors = processors.Where(x => x.CanProcess(request, response)).ToArray();
if (!matchingProcessors.Any())
{
return null;
}
return matchingProcessors[0];
}
}
// Individual request processors, you will have many of these...
public class StepA: IProcessor
{
public bool CanProcess(Request request, Response response)
{
// Validate wether this can be processed -- if condition here
return false;
}
public bool Handle(Request request, out Response response)
{
response = null;
return false;
}
}
在这种情况下,老式的C程序员使用goto。这是goto的一种用法,实际上是Linux样式指南鼓励的,它被称为集中函数exit:
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;
}
有些人使用goto的方法是将body包装成一个循环并将其断开,但实际上这两种方法做的是同一件事。如果你只在executeStepA()成功时才需要一些其他的清理,那么goto方法会更好:
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;
}
使用循环方法,在这种情况下,您将得到两级循环。
正如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》。