考虑一个应该提供参数建议的类,给出一些线索和特定的验收测试。
举例说明: 假设您根据文件名猜测原始数据文件的立方体尺寸。验收测试是:总元素==文件大小(假设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具有可维护性的优势,因为控制逻辑完全处于底层:验收测试。
答案 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增加了一点混乱。所有这些方法都有变种,但我更喜欢第一种方法。