5/10/2008

Some advices for API designing

设计需要进化
API的价值就在于能够帮助你完成许多功能,但请不要忘记要持续的改善它,否则它的价值就会逐渐减少。而且要根据用户的反馈信息,来改善API,或者说是 对API进行演化,另外改进API时要注意的就是在不同版本间要保持兼容性,这点至关重要,它也是一个API是否成功的重要标识之一。 如果一个功能以API的方式公布出来,那么在发布以后,它的对外接口就已经固定,无论什么情况,都不能取消,而且一定要能够按照原有的约定功能正确执行。 如果API不能在版本间保持兼容性(译注:这里应该指的是向下兼容),用户将会难以接受这样一个千变万化的API,最终的结果只能是让用户放弃使用你的 API。如果你的API被大量的软件或者模块所使用,又或者被大型软件使用,这个兼容问题也就愈加严重。
通常情况下,你不知道用户如何使用API来完成工作。除非能够确认API的修改对用户的代码不会造成破坏,才可以考虑修改API。,反之如果API的改变 会影响用户现在的代码,就必须有一个足够的理由。只有意识到对API的修改,会严重的破坏用户现有的代码,在修改API时才会谨慎地,尽量地保证API兼 容性。修改API的时候要尽量避免以下几种情况,以免对用户的代码产生破坏:
降低方法的可见性,如将public变成proteced,又或者将protected变成private。
修改方法的参数,如删除一个参数、添加一个参数、或者是改变参数的类型,尤以后两者更为严重。
当API的设计和开发到了一定阶段以后,可能会发现以前的版本已经出现了一些问题,又或者需要添加新的功能,此时设计人员完全可以重新创建新的API,并 放到新的包中,这样就可以保证那些使用老版本的用户可以很轻松的移植到新版本上,而不会产生问题。请牢记一点:添加新的功能,请不要修改原有的内容。


API的设计目标

设计一个API要达到哪位目标呢?除了兼容性以外,也从Elliotte的讨论中提出一些目标。
API的正确性必须保证:
无论用户如何调用API,都不应该产生错误。

API的易用性:
API必须易于使用。通常易用性一向难以评价。但是有一个办法可以有效的提高易用性,就是编写大量范例代码,并将其很好的组织在一起,从而为用户提供API参考。(译注:个人认为一个好的FAQ可以提供各种API使用的范例。)
API必须易学:
很大程度上,API的易学和易用性是相似的,一般来说,易用也就易学。如果要使API易学,下列基本原则要遵循的:
API越小就容易学习;
文档应该有范例;
如果方便的话,尽可能将API与一些常用的API保持一致。例如如果要做一个资源访问的API,尽可能与J2SE中的IO使用一致,自然很容易学习。
(译注:如果你要关闭一个资源,就象Java的File,Connection一样,使用close,而不是destroy。)

API的运行速度必须够快:
Elliotte也是考虑了很久,才给出这一条。但是要在保证API简单而且正确的前提下,再来考虑API的性能问题。

API必须足够的小:
这里所说的小不仅是指编译后代码的文件比较小,而且更重要的是运行时占用的内存要小。之所以提出最小化的概念,还有一个原因:就是因为很容易为API添加 新的内容,但是要将一个内容从API中移出就很困难,所以不要随便向API中添加内容,如果不确定一项内容,就不要将它加入到API中。通过这样一个建议 或者说是限制,可以提醒一个API设计人员更加关注API中最重要的功能,而非一些枝节的问题。

因为考虑的过于详细,功能边界也就越大,所面对的需求也就越多,因此要提供的功能和可供用户调整的功能也就更加庞大,也就是说这种方法会使得API包含很多的功能,最终就是API膨胀性的增长。



如果设计的API过于庞大,必然会包含了许多不必要的方法,至少有许多public的类和方法,对于大部分用户是用不到,也会占用更多的内存,并降低运行效率。这违反了通常的一个设计原则:“不要让用户为他使用不到的功能付出代价”。




下面在设计API时的一些技巧
不变类是一个很好的设计,如果一个类可以设计成不变类,就不要用可变类!如果详细了解这样设计的原因,请参见《Effective Java》的第十三条。如果没有读过这本书,很难设计出好的API。

另外字段信息应该是private的,只有static和final修饰的字段信息才能变成public,允许外部访问。这一条是一个非常基础的原则,这里提到这个原则,只是因为在早期的API设计时,有些API违反了这个原则,这里不再给出一个例子了。

避免奇怪的设计。对于代码,已经有了许多约定俗成的方法了,如get/set方法,标准的异常类。即使觉得有了更好的方法,也尽量避免使用这些方 法。如果使用了一些奇怪的方法名称,这样使用API的用户必须学习新的内容,不能按照原有的习惯来理解代码,会增加学习成本,也会增加误用的可能。

一个类如果不是抽象类,就应当是final类不可被继承。《Effective Java》第15章给出了足够的理由,同时也建议每个方法在默认情况下都应该是final(目前Java正好相反)(译注:这点我也赞成,觉得方法默认为 final更好,但是目前Java发展到当前情况下,已经不可能大规模的更改了,不能不说是Java语言的一个遗憾之处,C#这一点处理的更好,默认为 final,后面的留言也提到这个了)。如果一个方法可以被覆盖,一定要在文档中清楚的描述这个方法被覆盖后带来的后果,最好还能提供一些例子程序进行演 示以避免开发人员误用。

总结:
设计需要演化,否则会降低它的价值。
先保证API的正确性,在此基础上再追求简单和高效
接口并不如想像中的那么有用。
谨慎分包可以带来更多的价值。
不要忘记阅读《Effective Java》(译注:难道作者和Josh Bloch's有分赃协议不成。)

No comments: