Re-inventing Wheels- -| 回首页 | 2004年索引 | - -拿到了《与熊共舞》

The Dilemma of Exception- -

                                      

现在的异常体系设计是:系统内部所有异常继承同一个基类BaseException,BaseException又继承RuntimeException。在一个层次(例如DAO层或者Business Service层)中有一个异常基类,对于每种具体的出错情形再派生一个新的异常类,例如UserCannotFound、HaveNoPermission等等。
 
这样做的好处是避免了在接口上出现异常签名,因为“抛出哪种异常”是方法的私事。如果接口签名上指明详细的异常信息,又采用Checked Exception,当方法内部实现改变时,接口就会受到影响;如果接口签名上只泛泛地标注一个高层次异常(例如Exception甚至Throwable),又起不到任何提示的效果。
 
但是接口上不留异常签名又带来另一个问题:client程序员要到运行时(甚至可能要过相当长的时间)才知道这个方法可能抛出哪些异常,给编写代码造成相当大的麻烦。我想的折中办法是:(1)方法稳定之后,在接口上标注异常签名,由于全部异常继承RuntimeException,即使接口异常签名改变也不会破坏client;(2)用一个拦截器把所有异常log到统一的地方,并提供足够的信息,方便调试。

- 作者: gigix 2004年03月30日, 星期二 10:22

Trackback

你可以使用这个链接引用该篇日志 http://publishblog.blogdriver.com/blog/tb.b?diaryID=97808

回复

- 评论人:Eric

Wed Mar 31 12:46:34 CST 2004  作者邮箱 

对不起对不起,刚才界面没反应,点多了,不好意思。

- 评论人:Eric

Wed Mar 31 12:44:54 CST 2004  作者邮箱 

我的处理是这样的,我以前也是在每一层设置异常基类,然后让client去捕捉,后来我发现这样好象不太妥当,我就让这些异常基类通通提升一个层次,让client去定义,底层的模块捕捉到异常之后,转译成高层的异常再抛出去,因为我觉得本来属于某一层的异常却要到低层模块去定义很别扭。不如在client层提供入口,让底层去使用,比较符合ocp的原则,不知道对是不对。至于RuntimeException和checked Exception的问题我觉得如果底层有RuntimeException抛出来肯定是哪里有问题了,还不如让它quick and clean的停下来,错误再积累就不好了。
其实我也参加工作没几个月,菜鸟一只,经验谈不上,一点小小意见,如果错了请不要客气。

- 评论人:Eric

Wed Mar 31 12:44:49 CST 2004  作者邮箱 

我的处理是这样的,我以前也是在每一层设置异常基类,然后让client去捕捉,后来我发现这样好象不太妥当,我就让这些异常基类通通提升一个层次,让client去定义,底层的模块捕捉到异常之后,转译成高层的异常再抛出去,因为我觉得本来属于某一层的异常却要到低层模块去定义很别扭。不如在client层提供入口,让底层去使用,比较符合ocp的原则,不知道对是不对。至于RuntimeException和checked Exception的问题我觉得如果底层有RuntimeException抛出来肯定是哪里有问题了,还不如让它quick and clean的停下来,错误再积累就不好了。
其实我也参加工作没几个月,菜鸟一只,经验谈不上,一点小小意见,如果错了请不要客气。

- 评论人:冰云

Tue Mar 30 21:05:40 CST 2004  作者邮箱  作者Blog

我现在的异常处理和你做的差不多。每一层都有BaseUnckeckedException,但有时候也用CheckedException。以前在onjava看到过一篇讨论受它影响比较深。对于无法处理的异常,就用UnChecked,例如SQL错误,产生于DAL,对于系统的来说是致命的。交给BusinessLayer毫无用处,因此抛出DaException。
如果是有用的可处理的例如处理用户登录的时候,UserNotExistException或PasswordNotVerfiedException就用CheckedException,交给Client去处理,显示不同的信息。

另外,在处理异常方面,我现在采用的是Struts1.1的ExceptionHandler机制。在ExceptionHandler捕获所有的Throwable,然后交给一个统一的或者按照module区分的Error.Jsp页面来显示。

另外有一个问题就是i18n在异常中如何处理。抛出的异常最终会显示到界面上。如果不做任何i18n处理恐怕不够友好。因此我让所有的自定义Exception实现一个ErrorKeyable接口。包含的Error就是i18n的key。如果我在ExceptionHandler中得到的Exception是ErrorKeyable的实例,那么就显示定制的错误信息。如果不是,就显示Unkown Error,呵呵。然后做个按钮在错误页让用户提交给admin。

- 评论人:透明

Tue Mar 30 14:03:20 CST 2004 

“使用这些接口的地方不是都要做相应变化了吗”,不是的,因为我用的都是unchecked exception,就算接口上声明了,client同样可以不catch。

- 评论人:郁也风

Tue Mar 30 12:56:35 CST 2004  作者邮箱  作者Blog

对于异常处理,原本我从来没把它当一个问题,用的处理方式也一般都是参照thinking in java中的介绍。可后来在hibernate中文论坛看到robbin和其他人对于check exception的讨论之后才发现异常处理确实是一个很让人头大的问题,后来又找到了tij作者写的一个关于ce的文章,最后的感觉是看的越多人越晕,到现在又看到了你的这篇了。看了这么些文章之后,最大的感受是,不管正方、反方都是言之成理,因为此也就更让我不知该如何处理了。(如你这篇文章有个例子啥的就很完美了)
我现在比较头疼的是,哪些异常应该抛出,哪些可以不抛(如将connection返回链接池时出现错误,而实际 的处理已经可以返回正确结果),如果异常出现嵌套,又该如何去进行深度捕获,另外就是异常应该以何种面目抛出,对于抛出的异常信息之后又该如何对用户进行引导,等等等等。这些东西一思考发现其实难度还是蛮大的,不是像当初直接抛一个出错页面就可以了戏的。

对于你提出的两个折中方案,我有些疑惑:
对于2还好说,可以使用一个专门的异常处理方法,在将异常作为参数传递进去,然后再进行日志处理等操作即可。
而对于1,我就很是迷惑了。你所谓的“在接口上标注异常签名”是指在接口后面加上throws?如果这样的话,使用这些接口的地方不是都要做相应变化了吗?而且,我的观念中接口最好是开始就定义完成,或是在重构时根据需要提升接口,如果在client都已经完成后再修改接口,岂不是带来大量工作量?
一些极不成熟的想法,作为java低手,这些也确实对我造成了困扰,还请不吝解惑,3kx

评论内容: