美文网首页
API设计的一些核心原则

API设计的一些核心原则

作者: 时芥蓝 | 来源:发表于2020-02-22 22:02 被阅读0次

    没有经过设计的代码,是没有灵魂的。


    鲁迅没说过这句话

    设计涵盖的内容很广,而API设计是与日常开发关系比较紧密的一块。换句话说,掌握了一些核心的设计要领,对代码质量提升是立竿见影的。

    无规矩不成方圆,API设计一样。最近学习了Google工程师总结的API设计原则,看完后,脑海中回忆起自己写的代码,方法的命名、参数顺序、异常处置、功能范围,无一不被作者所戳中。我觉得很有价值,所以拿来与大家一起分享,相信对你肯定有帮助。当然,作者总结的比较简洁,具体含义,需要大家思考并深入的体会,相信经过一番思考以后,定有收获。

    由于原文是生肉,为了给部分小伙伴提供一些便利,我进行了翻译,翻译不好的地方还请指正,在此表示感谢。当然有喜欢生肉的同学,我也会在文末评论处贴上原文地址,请自取。

    以下是正文部分。


    image

    API设计原则

    通用原则

    api应该只做一件事,并且要做好

    • 功能应易于解释
    • 如果很难对一个功能命名,通常是一个坏兆头
    • 好名称驱动开发
    • 模块能够拆分和合并

    最小化内容访问权限

    • 类和成员变量尽可能私有化
    • 公共类不应该有公共变量(常量除外)
    • 最大化信息隐藏
    • 允许模块能够独立的使用、理解、构建、测试、调试

    名字很重要 -api是一种小语言

    • 命名在很大程度上应该是不言自明的(避免过于隐晦的缩写)
    • 保持一致,同一个词应该始终代表一个含义(在整个api、跨平台的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设计决策对性能的影响是真实并且持久的

    • Component.getSize() 返回 Dimension
    • Dimension是可变的
    • 每次调用getSize()将会分配一个Dimension
    • 导致数百万不必要的对象
    • 即使在1.2版本中增加新方案,老的代码依旧执行很慢

    api必须与平台和平共存

    • 按规律办事
      • 遵守标准的命名约定
      • 避免废弃的参数类型和返回类型
    • 利用好api的特性
      • 泛型、枚举、默认参数、varargs
    • 了解并避免api的陷阱
      • Finalizers, public static final arrays

    类设计

    减少可变性

    • 类应该是不可变的,除非有更好的理由
      • 优点:简单、线程安全、可重用
      • 缺点:每个值都有单独的对象
    • 如果是可变的,保持状态空间尽可能小和良好的定义
      • 明确何时调用那个方法是合法的

    Bad: Date, Calendar
    Good: TimerTask

    子类只出现在有意义的地方

    • 子类化意味着可替代性
      • 仅当is_a关系才出现子类
      • 否则,请使用组合
    • 公共类不应该仅仅为了容易实现而子类化其他公共类

    Bad: Properties extends Hashtable, Stack extends Vector
    Good: Set extends Collection

    设计和文档用于继承,否则就禁止它

    • 继承违反了封装原则,子类对超类的实现细节敏感
    • 如果你允许子类化,文档留作自用
    • 保守策略:所有的具体类都final化

    Bad: Many concrete classes in J2SE libraries
    Good: AbstractSet, AbstractMap

    方法设计

    不要让使用者做任何模块可以做的事情

    • 减少对样板代码的需求
      • 通常通过剪切和粘贴来完成
      • 丑陋,烦人,容易出错
    image.pngimage.png

    不要违反最小化原则

    • api的用户不应该对api的行为感到惊讶
    • 这值得付出很大的努力,甚至牺牲一些性能
    public class Thread implements Runnable {
        // Tests whether current thread has been interrupted.
        // Clears the interrupted status of current thread.
        public static boolean interrupted();
        }
    }
    

    快速失败-当发生错误时,尽可能早的报错

    • 编译时最好- 静态类型、泛型
    • 运行时,首先调用发生错误的方法
      • 方法应该是原子失败的

    使用适当的参数和返回类型

    • 在输入时,优先选择接口类型而不是类
      • _提供灵活性、性能
    • 使用最具体的输入参数类型
      • 将错误从运行时移动到编译时
    • 如果存在更好的类型,不要使用string
      • 字符串是笨重的,容易出错的,而且很慢
    • 不要使用浮点数来表示货币值
      • 二进制浮点导致不精确的结果!
    • 使用double(64位)而不是float(32位)
      • 精度损失是真实的,性能损失可以忽略不计

    使用一致的参数排序

    • 尤其重要的是,如果参数类型相同
    • 如Collections中的方法,第一个参数一般都是被修改或被查询的。
    image.pngimage.png
    image.pngimage.png

    避免长的参数列表

    • 三个或更少的参数是理想的
      • 更多的用户将不得不参考文档
    • 一长串类型相同的参数是有害的
      • _程序员误转了参数
      • 程序仍在编译、运行,但行为不正常!
    • 两种缩短参数列表的技术
      • 拆分方法
      • 创建助手类来保存参数

    避免需要异常处理的返回值

    • 返回零长度数组或空集合,而不是null

    Bad:


    image.pngimage.png

    异常设计

    支持unchecked异常

    •checked-客户必须采取恢复措施
    •unchecked–编程错误
    •过度使用checked的异常会导致样板


    image.pngimage.png

    在异常中包含失败捕获信息

    • 允许诊断和修复或恢复
    • 对于未检查的异常,消息就足够了
    • 对于已检查的异常,提供访问器

    相关文章

      网友评论

          本文标题:API设计的一些核心原则

          本文链接:https://www.haomeiwen.com/subject/yourqhtx.html