异常争论
异常有两个模型:中止模型和继续模型
中止模型认为异常不应该再回来,他做的是善后工作。而继续模型保持异常时环境,希望再一次能运行成功。
Java采用的是前者(一般语言都是前者),而OS一般采用后者。
Java异常有三类:错误,运行时异常,检查型异常。
官方的观点是
第 39 条:最好为异常条件使用异常。也就是说,最好不为控制流使用异常。
第 40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。
第 41 条:避免不必要的使用检查型异常。
第 43 条:抛出与抽象相适应的异常。(使处理异常更直观)
在异常的使用上,专家的观点是很不一样的
C#作者Anders根本就忽略检查型异常。
Bruce Eckel,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
缺点1,代码中包含了过多的catch,使得代码不清晰
缺点2,有时候捕捉的异常没有什么实际意义
缺点3,不够清晰的错误指示。
缺点4,过深的异常层次。
缺点4,性能。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Eckel 提倡将所有的异常都作为非检查型的,并且提供将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力
Rod Johnson ,他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
解决1:谨慎的抛出检查型异常。或者你认为,你可以处理它。否则,包装为运行时异常。
解决2:如果遵守1,2不是问题
解决3:异常不跨层,否则必须捕捉或者包装。
比如持久层丢出的SalException,你或者丢弃/处理/包装(为运行时异常),或者重新包装为业务层异常。保持JEE层的独立和异常的清晰性。
包装底层异常,保持异常链。
解决4:如果符合1,4也不是问题。再次强调,能捕捉就捕捉。
解决5:减少异常使用,减少层次。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
在je里面,robin认为异常是流程控制的一部分——当然,考虑到性能问题,这个流程不应该是大概率流程——也就是异常流程
例如用户登录
Try{
用户登录(用户名,密码);
登录成功;
}catch(没有这个用户异常 e){
错误提示界面;
}
Potian则认为,没有用户是正常业务逻辑的一部分
If(!用户业务层.没有这个用户(用户名))错误提示界面;
If(用户业务层.检验密码(用户名,密码))登录成功;
else 登录失败;
Potian认为不应该在一个业务中包含了过多的责任。
Ps:在servlet中,我喜欢仅仅简单的在action中调用最好一个业务层方法就可以完成此action的任务。这意味着我的servlet非常瘦,可以比较容易的被替换。如果采用了potian的办法,则意味着我要把业务层中的代码前移到servlet中来,这模糊了业务层的责任。解决的办法是回到老路子上来。
Ps:我还认为,没有异常的业务方法表达能力太弱,异常给了他们更丰富的表达能力。这使得业务层可以更丰富的表达业务意义。避免将业务责任分散掉。
我认为在业务层中,恰恰要包含足够的责任。不多也不要少(流程分支-2最好)。在别的层次中,要细致一点。