为什么API设计很重要?应该怎样去设计?

Joshua Bloch Principal Software Engineer - Google

查看在线英文原版

为什么API设计很重要

API很大程度上会成为一个公司的优势

  • 一个好的API设计可以减少开发者理解的时间

  • API不能停止对外访问,所以在设计阶段一定要做好设计

  • 坏的API也同样会为公司带来不利的影响

  • 对外的API是长久的,一处改变将会影响很多问题


为什么API设计对你很重要?

  • 如果你是API设计者,应该将API系统设计成模块化、组件化的

  • 模块化的代码将使得重用性大大提升

  • 好的API设计同样可以提升代码质量


优秀API的特点

  • 容易理解,可读性强

  • 容易使用,最好不用看文档就能理解API的作用

  • 容易阅读且可维护

  • 容易扩展


具体应该怎样去设计?

  1. API设计步骤

  2. 通常的原则

  3. 类设计

  4. 方法设计

  5. 异常该如何处理

1. API设计步骤

一定要全面的理解需求以及日后可能的需求变更

  • 通常你会得到建议性的解决方案,要记住,好的解决方案真的存在

  • 争取对收集到的需求进行细致性的划分

  • 好的设计一定是最简单的,简单即是美

在动手写之前要做什么?

  • 动手写之前要做好准备,你可能会推翻之前的设计

  • 动手写之前确保你已经有效的细化了API的细节

  • 持续不断地完成API,直到完成了所有需求

切实可行地处理各种异常

  • 争取尽量预见可能发生的错误

  • 你的API可能会使用很久,争取预见将来可能会遇到的坑,并解决它!

2.通常的原则

一个API应该只做一件事,并把该做的事做到最好

  • 功能应简单并容易理解

  • 如果很难命名,这通常是一个不好的信号

  • 好的命名能推进我们的开发工作

  • 一个API应是由多个模块组合而成,并且是有机的拆分

最小化每件事情

  • 确保类和类的成员尽可能私有化

  • 公开的类不应该有公开的成员,除了常量

  • 尽可能的对外隐藏类的信息

  • 保证每一个模块是可用的、可理解的、可强化的、可测试的和可debug的。

名字很重要,API就像一门小型的语言

  • 名字应该尽可能可以描述自己

  • 命名要有规律性

  • 代码应读起来像散文


if (car.speed() > 2 * SPEED_LIMIT) 

	generateAlert("Watch out for cops!");

文档很重要

可重用是一件说得容易做起来难的事情,能够做到可重用需要良好的设计和漂亮的文档,好的设计是很罕见的。但是如果没有好的文档,就绝不可能出现可重用的模块。 - D. L. Parnas, _Software Aging. Proceedings of 16th International Conference Software Engineering, 1994

API设计要考虑性能

哪些坏的决定会影响性能

  • 使用可变类型

  • 提供构造方法而不用工厂

  • 使用实施类型代替接口

不要为了提升性能,搞乱了API

  • 引起性能问题的问题可以解决,但扭曲的设计将永远存在

  • 好的设计通常会兼顾性能

API应与它所运行的环境相互融合

按照惯例做事

  • 服从标准

  • 避免特殊的参数和返回值

  • 效仿核心API和语言风格

3.类设计

最大限度的减小可变性

  • 除非给出一个不错的理由,否则设计好的类就不要再改动了

  • 如果一定需要改动,类要短小,定义明确,并且拥有清晰合理的函数调用

Bad: 

	Date,Calendar 

Good: 

	TimerTask

子类一定要有它存在的意义

定义子类意味着它的替代意义

  • 只有确实存在着有意义的关系(比如继承)的时候,才会定义子类

  • 另外,继承时也别忘了看看是否可以使用组合模式

公开的类不应该继承出子类

Bad: 

	Properties extends Hashtable 
	
	Stack extends Vector
Good: 

	Set extends Collection

如果使用了继承请一定要写设计文档

继承违反了封装

  • 因为继承导致子类对父类的实现细节敏感

  • 保守的做法:所有的类都应该是final的

Bad:

	Many concrete classes in J2SE libraries 
Good: 

	AbstractSet, AbstractMap

4.方法设计

不要让一个模块要做的事,被一个方法都干了,要做拆分

不要违反最小惊讶原则

当错误发生后,尽可能迅速的上报错误

使用恰当的参数类型和返回值类型

  • 使用更具体的参数类型

  • 如果有一种更好的类型存在,不要使用字符串

  • 在表示货币金额时,不要使用float,会导致精度问题

  • double(64 bits) 要好于 float(32 bits)

  • 贯穿整个方法,一致的参数类型和顺序是很重要的

  • 避免过长的参数列表,或者说参数数量应尽可能的少

  • 避免特殊的返回值,如果返回值为空,要返回一个array(),不要返回null

5. 异常该如何处理

抛异常意味着程序进入了异常的条件,但要给client一个平滑的处理结果。相反,也不要什么都不做,最起码打个日志还是可以的。

在异常中捕获失败的信息

  • 允许诊断、修复和恢复

  • 对于没有经过检查的异常,$e->message()就足够了

  • 而对于经过检查的异常,提供给调用方最好