论坛首页 Java企业应用论坛

Java 接口的异常设计疑惑

浏览 13566 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-11-12  
抛出抽象异常

比如 baseAbstractException-
下有两个异常 一个sysE一个bussE
然后所有异常从这两个异常扩展
0 请登录后投票
   发表时间:2009-11-12   最后修改:2009-11-12
czpsailer 写道
在接口有多个不同的实现类时,不同的实现又可能抛出不同的异常,这样就无法在接口声明中将这些具体异常全部声明出来了,这时候使用一个抽象的异常就比较可行了。可是如果这样的话,我觉的使用接口的地方,就无法明确到底会出现哪些具体的异常了。这样貌似异常的使用原则(尽量指定具体的异常)有些相违背了。


这个接口本身都不知道会抛出什么异常,为什么还要向上抛出呢?(除非是上层调用可以处理的异常),自己都不确定的,上一层更没法知道怎么处理你这些异常了。不如就在本层处理,或者转成运行时异常。
0 请登录后投票
   发表时间:2009-11-12  
看了各位高手的回复后有点自己的看法:不赞成为业务需要而设计自定义异常类的做法(如余额不足、没有权限等),因为异常处理的开销比较大。鄙人认为业务上的问题还是通过普通的逻辑判断进行处理比较妥当。
0 请登录后投票
   发表时间:2009-11-12  
Dreamer 写道
看了各位高手的回复后有点自己的看法:不赞成为业务需要而设计自定义异常类的做法(如余额不足、没有权限等),因为异常处理的开销比较大。鄙人认为业务上的问题还是通过普通的逻辑判断进行处理比较妥当。

与IO、数据库访问相比,1ms的消耗应该不在考虑范围内。

是否需要定义异常,与你的设计、职责划分很有关系:还是以取款为例,如果你要求别人在调用之前,必须先检查好金额、权限等所有异常,你可以不考虑异常。

但从程序的Robust、从简化使用的角度,一般还是要去考虑的,不能把所有的前置条件、后置条件都交给调用方
0 请登录后投票
   发表时间:2009-11-12  
pipilu 写道
czpsailer 写道
在接口有多个不同的实现类时,不同的实现又可能抛出不同的异常,这样就无法在接口声明中将这些具体异常全部声明出来了,这时候使用一个抽象的异常就比较可行了。可是如果这样的话,我觉的使用接口的地方,就无法明确到底会出现哪些具体的异常了。这样貌似异常的使用原则(尽量指定具体的异常)有些相违背了。


这个接口本身都不知道会抛出什么异常,为什么还要向上抛出呢?(除非是上层调用可以处理的异常),自己都不确定的,上一层更没法知道怎么处理你这些异常了。不如就在本层处理,或者转成运行时异常。

这个是在稳定性与灵活性上去权衡:有一个基础类,可以保持接口稳定,让不同使用场景自行去约定异常。只有通用接口才有这样的需求,一般是框架类。具体子类实现的时候,还需要与使用者自行去约定异常。

一般业务逻辑中,专用接口,则需要详细定义异常。
0 请登录后投票
   发表时间:2009-11-12  
统一异常类型,比如自定义一个 ServiceException
0 请登录后投票
   发表时间:2009-11-12  
具体问题具体分析了,业务规定啥样的异常就要抛啥异常,接口只是一个规则, 看具体的规则,如果你的接口比较通用那么抛出的异常也要通用,就是异常尽量要大一点就是exception 然后实现类抛的异常更具体一点,如nullpointexception等等
0 请登录后投票
   发表时间:2009-11-12  
joachimz 写道
pipilu 写道
czpsailer 写道
在接口有多个不同的实现类时,不同的实现又可能抛出不同的异常,这样就无法在接口声明中将这些具体异常全部声明出来了,这时候使用一个抽象的异常就比较可行了。可是如果这样的话,我觉的使用接口的地方,就无法明确到底会出现哪些具体的异常了。这样貌似异常的使用原则(尽量指定具体的异常)有些相违背了。


这个接口本身都不知道会抛出什么异常,为什么还要向上抛出呢?(除非是上层调用可以处理的异常),自己都不确定的,上一层更没法知道怎么处理你这些异常了。不如就在本层处理,或者转成运行时异常。

这个是在稳定性与灵活性上去权衡:有一个基础类,可以保持接口稳定,让不同使用场景自行去约定异常。只有通用接口才有这样的需求,一般是框架类。具体子类实现的时候,还需要与使用者自行去约定异常。

一般业务逻辑中,专用接口,则需要详细定义异常。


有道理。
这种情况下,我倾向于交由实现类来自行决定抛出运行时异常,使用接口的开发人员通过阅读文档来了解实现类可能会抛出的异常。避免异常声明给调用者带来负担。
0 请登录后投票
   发表时间:2009-11-12   最后修改:2009-11-12

    查看了一些关于Exception的讨论,发现对于如何何时处理异常和使用Checked Exceptions还是Unchecked Exceptions是讨论很激烈的话题呀!如何很好的处理和使用异常,的确是一件不容易的事情?好多时候容易使人感到疑惑!

    对于“疑惑1:在设计接口的时,对于接口方法何时需要声明抛出受检异常或者说所有的接口方法最后都声明抛出受检异常?” 我比较同意前面joachimz的观点。

joachimz写道
接口就是约定,异常是返回结果之一。

对于系统性异常,例如网络可能中断,未知的程序错误等等,这种处理成运行时异常,不作申明比较好,反正接收的人也没办法做任何处理

对于有业务性的错误,例如取款时,余额不够、权限不够、超出最大取款限制等等,这类错误,最好,每个场景,定义一个异常,在申明中逐一说明,总之,异常是帮助使用者了解、处理不同的错误场景。使用者可以根据错误类型的不同,给用户提供不同的处理方式和流程。当然,如果你提供给用户的只是提示信息,就没必要再区分类型了

异常处理方式上,还有一个办法是对异常编码,那就只需要一种类型了,类似SqlException。两种方式都可行,重点是要让约定更加明确、翔实


      接口可以为声明业务性的错误,也就是对于UseCase中的每个异常流声明一个Checked Exception。这个异常最好是抽象的(例如,一个从文件、数据库或者 JNDI 装载资源的方法在不能找到资源时,应该抛出某种 ResourceNotFound 异常,而不是更底层的 IOException 、 SQLException 或者 NamingException ,就像Bloch 在《Effective Java》中提出的第 43 条的提到的:“方法在遇到失败时应该抛出一个异常,但是该异常应该反映该方法做什么,而不是它如何做。”).

   public interface IBusiness{ 

         public void method() throws BusinessException1,BusinessException2,。。。;

   }

      要做到以上的情况,必须要对业务进行透彻的分析,清楚的知道正常的业务流程和会出现那些错误的业务流程(从业务上说的错误),这样才能设计出完善的接口来。否则,如果接口定义不完善,以后发现还有错误流程没考虑进去那以后改起接口来将是一个灾难呀。

      我个人的想法:是否能将业务尽量的细粒度化,使得容易明确业务的所有错误和正常的流程,来避免接口重构(还请各位大大指教)?

    在一些项目和开源的项目看到过的一种解决方式:在接口声明中使用一个抽象的业务异常类,在具体接口实现中抛出这个抽象类的子类。
    
    public interface IBusiness{

          public void method() throws AbstractBusinessException;

    }

    public class BusinessImplA implements IBusiness{

         public void method() throws SpecificBusinessException1 {
               //业务操作
         }
  
    }

    public class BusinessImplB implements IBusiness{

         public void method() throws SpecificBusinessException2 {
               //业务操作
         }
  
     }

     注:AbstractBusinessException继承自Exception,SpecificBusinessException1,SpecificBusinessException2 继承AbstractBusinessException。

 

     使用方式一:

 

         每个具体的业务异常类(如SpecificBusinessException1这样的)本身表示具体的一个业务错误,而接口声明的AbstractBusinessException只是为了能在子类中抛出具体的业务异常。带来的好处之一是当以后发现需要添加新的业务错误(添加新的业务异常)时候,无需修改客户端代码。

 

         我觉得这种使用方式是不合理的,这样其实把一个接口方法的责任约定方式到了不同的地方(实现类的方式签名中)。接口的实现是无法预期的,客户端难以在出现异常时候确定具体是那个错误(除非客户端程序员能够获取到并且阅读所有的实现类(包括现在实现和将来实现)的API,但这是不可能的)并且进行相应的处理,此时的异常仅仅是对业务执行的错误的指示,无法成为进行恢复或近一步操作的指示。这种方式不值得提倡,因为我们应该在前期就透却全面的分析业务,避免出现遗漏业务流程的情况存在;而对于业务的变更,则应该选择重构。

 

     使用方式二:

 

         对异常编码,子类只是简单的包含不同的错误码,定义一个统一的异常字典。客户端获取异常后获取到错误码根据其进行相应处理。

 

         以下为耶鲁CAS的中的代码摘要:

 

         public abstract class AuthenticationException extends Exception {

             private String code;

             public AuthenticationException(final String code) {
                   this.code = code;
             }
             .....
         }

       public class BadCredentialsAuthenticationException extends AuthenticationException {

            public static final BadCredentialsAuthenticationException ERROR = new BadCredentialsAuthenticationException();

            private static final String CODE = "error.authentication.credentials.bad";

             public BadCredentialsAuthenticationException() {
                   super(CODE);
             }
             .....
       }

 

       我觉得这种使用方式就比较合理了,不过这种合理性同样是建立在前期对业务的透却全面的分析基础上。

 

      
    
    

 

0 请登录后投票
   发表时间:2009-11-12   最后修改:2009-11-12
pipilu 写道
joachimz 写道
pipilu 写道
czpsailer 写道
在接口有多个不同的实现类时,不同的实现又可能抛出不同的异常,这样就无法在接口声明中将这些具体异常全部声明出来了,这时候使用一个抽象的异常就比较可行了。可是如果这样的话,我觉的使用接口的地方,就无法明确到底会出现哪些具体的异常了。这样貌似异常的使用原则(尽量指定具体的异常)有些相违背了。


这个接口本身都不知道会抛出什么异常,为什么还要向上抛出呢?(除非是上层调用可以处理的异常),自己都不确定的,上一层更没法知道怎么处理你这些异常了。不如就在本层处理,或者转成运行时异常。

这个是在稳定性与灵活性上去权衡:有一个基础类,可以保持接口稳定,让不同使用场景自行去约定异常。只有通用接口才有这样的需求,一般是框架类。具体子类实现的时候,还需要与使用者自行去约定异常。

一般业务逻辑中,专用接口,则需要详细定义异常。


有道理。
这种情况下,我倾向于交由实现类来自行决定抛出运行时异常,使用接口的开发人员通过阅读文档来了解实现类可能会抛出的异常。避免异常声明给调用者带来负担。


这样其实有个问题,因为你无法阅读到所有的实现类的API文档和无法很好的预期运行是客户端会调用的那个实现类。

例如:当前已知有2个实现类声明了3个具体的异常。此时你的客户端程序可以很好的处理这些异常,当时如果那天有了一个新的实现类并且他声明了一个新的异常,你的客户端就不知道应该如果处理了。

不过貌似对于一个通用的接口,一般比较难对所有的实现进行预期,也就无法在接口定义的时候就声明所有的异常,那么是声明一个比较抽象的异常还是使用非受检异常呢,那就仁者见仁了。
  
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics