考虑一下这段代码(特别是Java):
public int doSomething() { doA(); try { doB(); } catch (MyException e) { return ERROR; } doC(); return SUCCESS; }
在哪里doB()
定义为:
private void doB() throws MyException
基本上,MyException
仅在doB()
满足某些条件的情况下存在(这不是灾难性的,但确实需要以某种方式提高这个条件),以便doSomething()
知道退出时出错.
你是否发现使用异常,在这种情况下控制流量,可以接受?或者这是代码味道?如果是这样,你会如何重构这个?
在doB()失败时执行doC()真的很重要吗?如果没有,为什么不简单地让Exception在堆栈中向上传播到可以有效处理的位置.就个人而言,我考虑使用错误代码代码气味.
编辑:在您的评论中,您已经准确地描述了您应该简单声明的情节
public void doSomething() throws MyException
它完全取决于错误条件是什么,以及方法的工作是什么.如果返回ERROR
是处理调用函数错误的有效方法,为什么它会变坏?
然而,通常它是一种气味.考虑一下:
bool isDouble(string someString) { try { double d = Convert.ParseInt32(someString); } catch(FormatException e) { return false; } return true; }
这是一个非常大的代码味道,因为你不期望双重值.您只想知道字符串是否包含double.
有时,您使用的框架没有其他方法来执行您想要的操作.对于上述,有一个更好的方法:
bool isDouble(string someString) { bool success; Convert.TryParseInt32(someString, ref success); return success; }
这种例外有一个特殊的名字,由我最近阅读的博客创造.但遗憾的是,我忘记了它的名字.如果你知道的话请评论.最后但并非最不重要的是,上面是伪代码.我不是#开发人员,所以上面的代码不能编译,我敢肯定,但是TryParseInt32/ParseInt32证明了这一点,我认为我会选择C#.
现在,到您的代码.我们来检查两个功能.一个闻起来,另一个没有:
public int setupSystem() { doA(); try { doB(); } catch (MyException e) { return ERROR; } doC(); return SUCCESS; }
这是代码味道,因为当您想要设置系统时,您不希望它失败.未能设置系统意味着您无法在不处理该错误的情况下继续操作.
public int pingWorkstation() { doA(); try { doB(); } catch (MyException e) { return ERROR; } doC(); return SUCCESS; }
没关系,因为该方法的目的是测试工作站是否仍然可以访问.如果不是,那么这是该方法结果的一部分,而不是需要替代返回路径的特殊情况.
我对OP代码的唯一问题是你混合范式 - doB通过抛出异常显示错误,而doSomething通过返回代码显示错误.理想情况下,你会选择其中一个.当然,在传统维护中,您可能没有那么奢侈.
如果您选择返回错误代码,那没关系,但我不喜欢它,因为它鼓励您使用辅助通道(如全局变量)在失败时通信状态,而不是将该状态捆绑到异常中并让它在堆栈中冒泡直到您可以做些什么.
我从来不喜欢使用控制流的异常(在某些语言中,如CLR,它们很昂贵).
如果你可以修改doB(),最好的办法是改变它以返回一个表示成功或失败的布尔值,所以你的例子看起来像:
public int doSomething() { doA(); if (!doB()) { return ERROR; } doC(); return SUCCESS; }
在以下情况下应使用例外:
一个函数无法正常完成,并且
当没有可用于指示失败的返回值时,或
当抛出的异常传达的信息多于return FAILURE;
可能的信息时,例如,嵌套的异常等.
请记住,除了返回值或方法参数之外,所有异常都只是在代码的不同部分之间发送的消息.努力优化通过这些方法传达的信息与API的简单性之间的平衡.当只需要一个简单的SUCCESS/FAILURE标志(并且该方法不需要返回其他值)时,请使用它.如果方法已经必须返回一个值,那么通常需要使用一个异常(这是一种查看它的方法,只是方法中的"异常"返回值).如果必须传递的故障信息太丰富而无法在返回值中传递(例如,I/O故障的原因),请使用异常.
最后,错误处理是一个设计决策,并没有一套适合所有情况的规则.