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

时间:2011-11-09 11:23:55

标签: c++ design-patterns readability

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

举例说明: 假设您根据文件名猜测原始数据文件的立方体尺寸。验收测试是:总元素==文件大小(假设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;

内部验收测试:

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

方法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_;

验收测试:

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;
}

方法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;

测试功能和验收测试与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;

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


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

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

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


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

这让我觉得方法1具有可维护性的优势,因为控制逻辑完全处于底层:验收测试。

3 个答案:

答案 0 :(得分:5)

方法5:短路逻辑

if (someTest1() ||
    someTest2() ||
    // ...
    someTestN()) 
{
    // success    
}

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

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

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

答案 1 :(得分:3)

嗯,我真的会选择最简单的解决方案:

bool succeeded;

if (!succeeded)
    succeeded = someTest1();
if (!succeeded)
    succeeded = someTest2();
if (!succeeded)
    succeeded = someTest3();
if (!succeeded)
    succeeded = someTestN();

我可以争论其他解决方案,但可以说是太棒了:简单好,复杂坏。

答案 2 :(得分:0)

我认为第一种方法比其他方法更易读,基于c ++并且最容易维护。 Gotos和do-while-false增加了一点混乱。所有这些方法都有变种,但我更喜欢第一种方法。