ROSE 编译器框架/良好 API 设计
外观
Google: Joshua Bloch 著的“如何设计一个好的 API 以及它为什么重要”
待办事项:从 Markdown 转换
- 易于学习
- 易于使用,即使没有文档
- 难以误用
- 易于阅读和维护使用它的代码
- 足够强大以满足需求
- 易于扩展
- 适合受众
- 以用例的形式收集真实需求
- 从简短的 1 页规格说明开始
- 敏捷胜过完整性
- 收集大量反馈
- 尽早且经常使用您的 API
- [测试驱动开发 (TDD)](http://en.wikipedia.org/wiki/Test-driven_development)
[T]重复一个非常短的开发周期:首先,开发人员编写一个失败的自动化测试用例,定义所需改进或新功能,然后生成通过该测试的代码,最后将新代码重构到可接受的标准。
- 同时作为示例/教程和单元测试
- 保持现实的期望
- 你不能取悦所有人……目标是让每个人都同样不满意
- 预计 API 会发展;会犯错误;需要真实世界的使用
- 如有疑问,请勿使用。你可以随时添加,但不能删除。
- 仅仅因为你可以,并不意味着你应该
- [功率重量比](http://en.wikipedia.org/wiki/Power-to-weight_ratio)
> [A] measurement of actual performance [power / weight]
- 不要给用户一把枪让他们自伤
- 信息隐藏:最大程度地减少所有内容的可访问性
- 类:实例代表什么
* Method: contract between method and calling client (preconditions, postconditions, and side-effects) * Parameter: indicate units, form, ownership
先决条件和后置条件
- 先决条件语句表示在调用函数之前必须为真。
- 后置条件语句表示在函数完成工作时将为真。
/// \post <return_value>.empty() == false
实现细节不应影响 API。不要让实现细节“泄漏”到 API 中。
性能
- 为可用性设计,为性能重构
- 不要为了提高性能而扭曲 API
- API 设计决策对性能的影响是真实且永久的
- Component.getSize()返回值Dimension
- Dimension是可变的
- 每次getSize调用都必须分配Dimension
- 导致数百万次不必要的对象分配
- API 必须与平台和平共处
- 做习惯的事(标准)
- 避免过时的参数和返回值类型
- 模仿核心 API 和语言中的模式
- 利用 API 友好功能:泛型、可变参数、枚举、默认参数
- 不要让客户端做模块可以做的事情
- 减少对样板代码的需求
- 不要违反[最小惊讶原则](http://en.wikipedia.org/wiki/Principle_of_least_astonishment)
> The design should match the user's experience, expectations, and mental models...aims to exploit users' pre-existing knowledge as a way to minimize the learning curve
- 提供对所有以字符串形式提供的数据的编程访问权限 => 无需客户端字符串解析
- 小心重载:模糊重载
- 基本上不言自明(避免使用神秘缩写)
- 保持一致(例如,相同的词语意味着相同的事情)
- 努力追求对称
- 应该像[散文](http://en.wikipedia.org/wiki/Prose)一样阅读
> [T]he most typical form of language, applying ordinary grammatical structure and natural flow of speech rather than rhythmic structure (as in traditional poetry).
if (car.speed() > 2 * SPEED_LIMIT) generateAlert("Watch out for cops!");
- 接口类型优于类:灵活性、性能
- 最具体的类型:将错误从运行时移动到编译时
- 使用double(64 位)而不是float(32 位):精度损失是真实的,性能损失可以忽略不计
- 一致的排序:
#include <string.h> char *strcpy (char *dest, char *src); void bcopy (void *src, void *dst, int n); // bad!
- 简短的参数列表:3 个或更少;更多的话,用户就必须参考文档;类型相同的参数有害
- 缩短参数列表的两种技巧:1)分解方法,2)创建辅助类来保存参数
- 避免需要特殊处理的值
> For example, return a `zero-length array` or `empty collection`, not `null`
- 不要强迫客户端使用异常进行控制流
- 不要静默失败
- 更喜欢未经检查的异常
- 包含故障捕获诊断信息
- 快速失败:尽快报告错误
- 编译时:静态类型、泛型
- 运行时:在第一个错误方法调用时出错(应该是故障原子)