科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道Java API设计指南1

Java API设计指南1

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

市场上关于如何设计和编写优秀Java代码的书如此之多,可能要用汗牛充椟来形容,但是想找到一本如何设计API的书,却是难之又难。这里我将把自己一些关于API设计的经验与大家分享。

作者: 王磊 来源:IT专家网 2008年5月7日

关键字: 指南 设计 API java

  • 评论
  • 分享微博
  • 分享邮件
前言

  市场上关于如何设计和编写优秀Java代码的书如此之多,可能要用汗牛充椟来形容,但是想找到一本如何设计API的书,却是难之又难。这里我将把自己一些关于API设计的经验与大家分享。

  分享这些经验是源于最近我参加了JavaPolis上的一个讨论,这个讨论是由Elliotte Rusty Harold发起的,是关于设计XOM时的一些原则性问题,讨论中的思想交流如此精采,令我受益颇多。虽然这次讨论主题是与XOM有关,但是大部分的时间我们都在讨论设计XOM API时的一些原则性问题,而这些内容对于API设计而言,则是通用的。这几年,Java的应用日益广泛,开源项目也是蒸蒸日上。一本能够指导开发人员设计和编写API的好书,可以帮助开发人员设计和编写更好的API。

  在过去的五年中,我一直参与JMX API的修订及调整,在此过程中,同样受益颇多。特别在这次讨论会,我对Elliotte提出的一些观点高举双手赞同。

  接下来的内容是讨论会上的一些主要观点的总结,其中包括一些个人或者是来自他人的经验,也参考一些相关的文档,希望对大家设计API有所裨益。

  下面是个人推荐的一些阅读和参考资料。

  下面给出的网址是Netbeans网站上的一篇关于API设计的优秀文档,

  http://openide.netbeans.org/tutorial/api-design.html

  Josh Bloch's 的Effective Java作为Java设计的圣经之一,从来都不会被漏下。

  设计需要进化

  API的价值就在于能够帮助你完成许多功能,但请不要忘记要持续的改善它,否则它的价值就会逐渐减少。而且要根据用户的反馈信息,来改善API,或者说是对API进行演化,另外改进API时要注意的就是在不同版本间要保持兼容性,这点至关重要,它也是一个API是否成功的重要标识之一。

  如果一个功能以API的方式公布出来,那么在发布以后,它的对外接口就已经固定,无论什么情况,都不能取消,而且一定要能够按照原有的约定功能正确执行。如果API不能在版本间保持兼容性(译注:这里应该指的是向下兼容),用户将会难以接受这样一个千变万化的API,最终的结果只能是让用户放弃使用你的API。如果你的API被大量的软件或者模块所使用,又或者被大型软件使用,这个兼容问题也就愈加严重。

  以一个Java应用程序为例,如果Module1使用了Banana1.0的API,而Module2则使用了Banana2.0的API,现在要同时部署这两个模块到一个Web Application上,如果Banana2.0对Banana1.0保持兼容的话,整个部署就会非常简单,直接使用Banana2.0就可以了。如果Banana2.0和Banana1.0不兼容的话,就很难同时在一个程序中同时使用Module1和Module2(可能要自定义ClassLoader,或者是其它方式,这对于用户未免有些为难了)。最终的结果可能就是你失去一个用户,这时的API就不能为他人提供任何价值(译注:看来它也不能带来经济价值了)。

  分析Java SE平台所提供的API,有着严格的兼容性控制。其根本目标就在于保证用户在版本升级时,不会导致低版本的代码无法运行。这也再次说明,当一个API发布以后,任何API公开的方法,常量等元素都不能被移出API,否则会严重降低API的价值。(译注:所以个人一向比较怀疑deprecated的价值,因为既然所有的方法都不会被删除,即使那些被 deprecasted的方法和变量也会被仍然保留,其功能也不应该会被改变,因此从这个角度来说,其方法和变量的使用仍然是安全的。)

说到兼容性,必然要谈到二进制兼容性,这是指对于已经编译完成的代码,不会因为版本的变更,而致使编译后的代码不能正常运行。比如说你将一个public方法从API中移走,就会无法正常运行,会抛出NoSuchMethodException这类错误。

  但源代码的兼容性也不可忽视,在某些特殊情况下,修改API时,会产生源代码兼容问题,导致源代码无法编译通过。例如添加一个重载的同名方法,其参数不同,如getName(User)和getName(String),当用户使用getName(null)时,会因为存在二义性,而产生编译错误,用户必须给出getName((String)null),来明确标识要调用的方法。因此必须寻找一种方式,来保持源代码的兼容性。

  通常情况下,如果源代码不兼容,代码编译就无法通过,用户必须修改源代码才能保证程序正常运行,但这并不是一个好的解决方案。以J2SE6中的javax.management.StandardMBean为例,这个类的构造函数已经采用泛型了。这样会使得一些类在创建这个Bean的时候,会出现编译不能通过的问题,但是解决方案有时却是非常简单,往往只需要添加一个cast进行强类型转换就可以解决这样的一个编译问题了。但是更多的时候,需要更复杂的方案才能解决这些编译问题,例如在一个方法中调用第三方的API方法,如果有一天这个API方法的参数被修改了,在修改源代码时候,可能会发现你的方法中不含有API方法需要的参数,这时就需要修改方法参数。以下是这种情况的示范代码。

  public void callApi(Parameter1 p1,Parameter2 p2)
  {
  //do some operations
  new ThirdClass().doCallThirdApi(p1);
  }

  现在第三方的API现在变成了doCallThirdApi(Parameter1 p1,Parameter3 p3)

  这时可能需要将方法改成:

  public void callApi(Parameter1 p1,Parameter2 p2,Parameter3 p3)
  {
  //do some operations
  new ThirdClass().doCallThirdApi(p1,p3);
  }

  这样一个API的改变,很可能引发一个多米诺骨牌事件。

  通常情况下,你不知道用户如何使用API来完成工作。除非能够确认API的修改对用户的代码不会造成破坏,才可以考虑修改API。,反之如果API的改变会影响用户现在的代码,就必须有一个足够的理由。只有意识到对API的修改,会严重的破坏用户现有的代码,在修改API时才会谨慎地,尽量地保证API兼容性。修改API的时候要尽量避免以下几种情况,以免对用户的代码产生破坏:

  降低方法的可见性,如将public变成package或者proteced,又或者将protected变成private。

  修改方法的参数,如删除一个参数、添加一个参数、或者是改变参数的类型,尤以后两者更为严重。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章