成功时抛出与假冒伪劣对比函数数组与goto语句

swa*_*log 3 c++ design-patterns readability

考虑一个应该提供参数建议的类,给出一些线索和特定的验收测试.

concretise的示例: 假设您根据文件名猜测原始数据文件的立方体尺寸.验收测试是:总元素==文件大小(假设1字节pr.网格单位).

这需要优先排序测试,其中每个测试进行一次或多次尝试通过验收测试.通过的第一个建议立即返回,不再尝试.如果没有通过,建议不要.

问题:当可读性是主要问题时,您会推荐哪种模式/方法?此外,以下建议有哪些缺陷和缺点?


方法1:获得成功验收测试的例外情况

我听说智者会在没有捕获实际异常的情况下避免使用try/catch.但是,在这种情况下,结果是相当可读的,看起来像:

try {
  someTest1();
  someTest2();
  // ...
  someTestN();
}
catch(int){
  // Succesfull return
  xOut = x_; yOut = y_; zOut = z_;
  return;
}
xOut = -1; yOut = -1; zOut = -1;
Run Code Online (Sandbox Code Playgroud)

通过内部验收测试:

void acceptanceTest(const int x, const int y, const int z)
{
  if (verify(x * y * z)) {
    x_ = x;   y_ = y;  z_ = z;
    throw 1;
  }
}
Run Code Online (Sandbox Code Playgroud)

方法2:Do-while-false:

更改:所有测试在通过验收测试后立即返回true.如果测试中的所有尝试都失败,则返回false.

do {
  if ( someTest1() ) break;
  if ( someTest2() ) break;
  // ...
  if ( someTestN() ) break;
  // All tests failed
  xOut = -1; yOut = -1; zOut = -1;
  return;
} while (0);
xOut = x_; yOut = y_; zOut = z_;
Run Code Online (Sandbox Code Playgroud)

验收测试:

bool acceptanceTest(const int x, const int y, const int z)
{
  if (verify(x * y * z)) {
    x_ = x;   y_ = y;  z_ = z;
    return true;
  }
  return false;
}
Run Code Online (Sandbox Code Playgroud)

方法3:函数指针数组

typedef bool (TheClassName::*Function)();
Function funcs[] = { &TheClassName::someTest1,
                     &TheClassName::someTest2,
                     // ...
                     &TheClassName::someTestN };

for (unsigned int i = 0; i < sizeof(funcs)/sizeof(funcs[0]); ++i) {
  if ( (this->*funcs[i])() ) {
    xOut = x_;  yOut = y_;  zOut = z_;
    return;
  }
}
xOut = -1;  yOut = -1;  zOut = -1;
Run Code Online (Sandbox Code Playgroud)

测试功能和验收测试与do-while-false相同.


方法4:转到

我已经看到do-while-false被称为伪装的goto,接着是如果这是预期的行为"为什么不使用goto?"的论点.所以我会列出来的:

if (someTest1() ) goto success;
if (someTest2() ) goto success;
// ...
if (someTestN() ) goto success;
xOut = -1;  yOut = -1;  zOut = -1;
return;

success:
xOut = x_;  yOut = y_;  zOut = z_;
return;
Run Code Online (Sandbox Code Playgroud)

测试功能和验收测试与do-while-false相同.


方法5:短路逻辑(Mike Seymour建议)

if (someTest1() ||
    someTest2() ||
    // ...
    someTestN()) {
    // success    
  xOut = x_;  yOut = y_;  zOut = z_;
  return;
}
xOut = -1;  yOut = -1;  zOut = -1;
Run Code Online (Sandbox Code Playgroud)

测试功能和验收测试与do-while-false相同.


编辑:我应该指出,方法2,3,4,5与1的不同之处在于要求将验证测试的布尔返回值一直传递回返回函数,以及每个测试函数中增加的开销.多次尝试通过验收测试.

这使我认为方法1具有可维护性的优势,因为控制逻辑仅处于底层:验收测试.

Mik*_*our 5

方法5:短路逻辑

if (someTest1() ||
    someTest2() ||
    // ...
    someTestN()) 
{
    // success    
}
Run Code Online (Sandbox Code Playgroud)

这相当于(并且在我看来更容易遵循)选项2和4,它们模拟其他流量控制操作的短路行为.

选项3非常相似,但更灵活; 如果你需要将相同的模式应用于不同的测试集,这可能是一个好主意,但如果你只有一组测试则是过度的.

选项1对很多人来说相当令人惊讶,因为例外通常只用于意外事件; 但是,如果测试的结构使得检测成功发生在深度调用链的某个地方,那么这可能比传递返回值更方便.它肯定需要记录,你应该抛出一个有意义名称的类型(例如success),并注意它不会被任何错误处理机制捕获.异常通常比正常函数返回慢得多,因此如果性能问题,请记住这一点.说了这么多,如果我想在这里使用异常,我当然会寻找简化测试结构的方法,以使返回值更方便.