有一个功能Foo。 Foo首先调用Bar(),然后调用一些语句,然后是Walk(),然后是一些语句,然后是Run(),然后是一些语句,然后是Fly(),然后是一些语句。
此外我不允许在我的代码中使用异常。因为我公司的代码标准。 :(
有几种方法,我可以编写Foo(),如下所示。有些很干净但不安全;有些人真的很难看,但记录了所有的日志。我真的很困惑。
哪一个最好,你有更好的想法吗?
任何人都可以帮助我吗?
我从五个方向考虑过这个问题:(清除[没有不必要的缩进]),(一次返回),(没有转到),(打印日志),(检查返回值)。但我仍然感到困惑。 goto真的有问题吗?是否有必要记录所有内容?
FIG1 优点:清洁。 缺点:没有日志&不检查返回值。
int Foo()
{
Bar();
// aStatement;
Walk();
// bStatement;
Run();
// cStatement;
Fly();
// dStatement;
return;
}
fig2 优点:清洁&日志。 缺点:多回报。
int Foo() {
int status;
status = Bar();
if (!OK(status)) {
Log("...");
return status;
}
// aSatement
status = Walk();
if (!OK(status)) {
Log("...");
return status;
}
// bStatement
status = Run();
if (!OK(status)) {
Log("...");
return status;
}
// cStatement
status = Fly();
if (!OK(status)) {
Log("...");
return status;
}
// dSatement
return status;
}
图三 优点:清洁&日志。 缺点:转到问题。
int Foo() {
int status;
status = Bar();
if (!OK(status)) {
Log("...");
goto RETLINE;
}
// aSatement
status = Walk();
if (!OK(status)) {
Log("...");
goto RETLINE;
}
// bStatement
status = Run();
if (!OK(status)) {
Log("...");
goto RETLINE;
}
// cStatement
status = Fly();
if (!OK(status)) {
Log("...");
goto RETLINE;
}
// dSatement
RETLINE:
return status;
}
图四 优点:使用do while,没有多重回报。 缺点:更多缩进。
int Foo() {
int status;
do {
status = Bar();
if (!OK(status)) {
Log("...");
break;
}
// aStatement
status = Walk();
if (!OK(status)) {
Log("...");
break;
}
status = Run();
if (!OK(status)) {
Log("...");
break;
}
// cStatement
status = Fly();
if (!OK(status)) {
Log("...");
break;
}
// dStatement
} while(false);
return status;
}
图5 优点:清洁&一回归。 缺点:没有记录。
int Foo() {
int status;
status = Bar();
if (Ok(status)) {
status = Walk();
// aStatement
}
if (OK(status)) {
// bStatement
status = Run();
}
if (OK(status)) {
// cStatement
status = Fly();
}
if (Ok(status)) {
// dStatement
}
return status;
}
图6 优点:日志&一回归。 缺点:更多缩进。
int Foo() {
int status;
status = Bar();
if (!OK(status)) {
Log("...");
}
if (Ok(status)) {
// aStatement
status = Walk();
if (!OK(status)) {
Log("...");
}
}
if (OK(status)) {
// bStatement
status = Run();
if (!OK(status)) {
Log("...");
}
}
if (Ok(status)) {
// cStatement
status = Fly();
if (!OK(status)) {
Log("...");
}
}
if (Ok(status)) {
// dStatement
}
return status;
}
fig7 优点:日志&一回归。 缺点:更多缩进。
int Foo() {
bool flag = true;
int status;
status = Bar();
if (!OK(status)) {
Log("...");
flag = false;
}
if (flag) {
// aStatement
status = Walk();
if (!OK(status)) {
Log("...");
flag = false;
}
}
if (flag) {
// bStatement
status = Run();
if (!OK(status)) {
Log("...");
flag = false;
}
}
if (flag) {
// cStatement
status = Fly();
if (!Ok(status)) {
Log("...");
flag = false;
}
}
if (flag) {
// dStatment
}
return status;
}
答案 0 :(得分:3)
答案 1 :(得分:0)
我使用规则:首先是有效性,然后是健壮性,然后是可维护性,然后在需要时效率:永远不要为了更清晰的代码而牺牲日志。当您的应用程序在某些情况下没有行走(或跑步或飞行)时,您需要日志。
从提案中,我建议使用早期返回,但与RAII结合使用,因此不需要在函数中进行清理。这非常接近使用例外。
BTW:使用goto有一个单独的退出点来清理并不是很糟糕,但在使用RAII时不再需要了。
答案 2 :(得分:0)
由于您的所有功能都具有相同的签名,您可以将呼叫和状态检查包装在一个功能中,并使用&&。
链接所有内容。int try_or_log(int (*f) (), const char** message) {
status = (*f)();
if(!Ok(status) {
Log(message);
return status;
}
int Foo()
{
return try_or_log(&Bar, "...") &&
try_or_log(&Walk, "...") &&
try_or_log(&Fly, "...");
}
(我的C ++生锈了,可能无法编译) 否则,这似乎是Monad的一个很好的候选者,所以你可以编写一个代码看起来像的对象(这可能值得谷歌'Monda in C ++'
int Foo() {
Status status;
status << &Bar << "..." << &Walk << "I don't walk" << &Fly << "I don't fly";
return status.status;
}
(实际上你可能需要2个类,所以&lt;&lt;运算符将根据输入(函数或日志消息)返回一个或另一个类。
答案 3 :(得分:0)
我个人会使用#2。我不认为退出函数会使执行路径变得复杂,足以证明您概述的任何其他方法。记录取决于您。更复杂的事情需要更多的记录。也许您可以将日志移动到可能失败的功能中? 你考虑过例外吗?
绝对不要使用break循环技巧。当你真正创建一个范围时,这似乎就是你的循环。
我不是100%反对goto。它对于快速修复很方便,但我不会设计代码来使用它。
旗帜似乎只是简单的额外包袱。
无论你决定坚持哪种惯例,
答案 4 :(得分:0)
如果你不能或不想使用例外,我会选择fig2或fig7。我从来没有见过一个很好的论据来反对一个方法中的多个返回,并且它做了你想要的其他一切,但如果你不喜欢这种方法或你有一个禁止它的风格指南,那么使用一个标志是可以接受的,尽管创建了一个对于这么简单的事情,不必要的变量。
使用do something() while(false)
似乎很愚蠢,因为你没有使用循环。
有些人对goto
非常虔诚,所以除非你别无选择,否则最好避免它,即使只是为了避免争论。它本身并不是邪恶的,但假设它可能更安全。
答案 5 :(得分:0)
如果你必须检查返回值,那么方法1就不够了。
方法2是可以接受的。
方法3是淫秽。
方法4-7只是方法2,过于复杂以避免多个return
,这些并不是那么糟糕。
如果您允许修改Bar()
,Walk()
等,请考虑例外情况。异常与此代码的行为不同,因为调用者可以忽略Foo
的返回值,而异常则不能;这可能适合您的目的,也可能不适合。