Java API 最佳开发实践 - Go语言中文社区

Java API 最佳开发实践


Java API 最佳实践

最近在做接口对接的工作,发现要写出一个双方都满意的api并不是一件容易的事,刚好在DZone 上看到微软工程师 Jonathan Giles 写的一篇文章,学到了一些经验,便做了翻译或许可以帮助到更多的开发者。
作为一个开发者,我们工作就是每天写代码,当然我们不可以脱离于其他人而写代码。可以确定的是我们都在踩在前人的肩膀上进行学习工作,今天有大量的工具可以供我们使用,
比如github,stack overflow,maven central,还有很多可用的第三方库可以供我们使用,软件是在api(Application Programming Interfaces)基础上创造的,我们使用大量来自Maven或者Gradle的第三方库。
如果我们询问工程师们他们是不是API开发者,他们通常会回答不是,但是这个回答有不正确的,工程师只要曾经精心写出过public的类或者public的方法,就可以被看作是api开发者。
“精心”这个形容词是必要的,API的设计就像一件艺术品,需要创造力并且需要付出很时间,同时更像是一门科学。

API风格

API风格有很多衡量的标准,下面介绍6点标准。

  1. 容易理解的
    你有多少次通过maven下载依赖的第三方库后,确不知道从那个类开始使用这个api? 一个设计优秀的api应该让使用者很直观的理解应该如何使用它。
    api开发者应该思考如果让使用者找到api的使用入口。完整的文档是帮助使用者理解api的全貌,但更理想的是在文档的开始处,使用最精炼的描述使使用者可以以小步迭代的方式学会使用api。
    因此,优秀的api试图通过使用入口暴露他们的功能点,使用者通过使用api start case而产生兴趣,然后通过文档掌握更多的功能特性。
  2. 良好的注释
    我们希望他人使用我们的api,因此我们需要努力把文档做好.
  3. 代码风格一致性
    一个好的api不应该使它的使用者感到意外,没有保持一致性便会使使用者感到意外。
    一致性是指我们在api中重复相同的风格,比如:
    3.1 所有方法使用getXYZ() 或者 xyz(),而不是同时使用两种形式
    3.2 如果有两个方法(一个是另一个的重载,比如一个参数为Object…,
    而另一个是Collectio)Collecti<? extends Object>),那么这种方式的重载风格需要保持下去。
    关键是在整个SDK的开发过程中我们需要确立一个小组一致赞成的术语规范和一个备忘录来确保风格一致性。
  4. 实现确定的目标
    开发api,我们必须确定满足用户的需求。可以从下面两个方式思考:
    4.1 只做一件事情,并且做好
    4.2 理解你的用户和他们的潜在目标
    JDK中的Collections API就是一个好的栗子,用户可以方便的利用集合存储数据和取出数据,而不需要自定义集合扩展条件、工厂、增加策略、hash碰撞策略、比较策略、缓存、负载系数等。开发者在使用集合的功能时甚至不需要了解其框架内部是如何工作的。
  5. 有约束的
    新增一个API或许会很快,但是我们应当尽力理解这个api,因为以后很可能会花很多时间来对这个api进行维护。
    对api的使用主要来自我们的项目和社区,一些项目经常做出一些破坏性的变更,然而像JDK本身的api很少出现紧急的变更。
    很多api会在中途被放弃,在api被移除之前使用一个语义上放弃的标志是一个合适的方式。
    一个项目在最终版本确定之前会使用很多标志来表明试验版、beta版、或者预习功能以备回滚使用。
    一个通常的做法在api被考虑周全之前使用@Deprecated标志或者清楚该标志来表明功能是否可以使用。
  6. 可维护的
    我们开发的每一个api,都是以开发者角度而不是用户的角度来确定其使用范围的。当我们确定api的使用范围时,必须花一些时间去验证在更宽广的上文中使用我们的sdk.

api就像一个合同

api可以被认为是一个合同,当其他开发者开始使用我们的api时,仿佛我们向他们承诺了一个可用的功能。对开发者而言,经常会考虑新的以及更好的实现方式(甚至是不起作用的实现),当我们需要修改我们的api以使其更好的工作时,同时会对使用者带来破坏性和bugs的风险,因此我们应该经过更加全面的考虑。
在创建1.0.0版本的api时,我们会体会到自由实验的感觉,在项目中,重点是我们达到了1.0.0版本后,直到2.0.0版本之前,它是无法被修改的。然而,在另外一些项目中,会相对灵活一点,允许持续迭代的修改api。实际上,一个明智的处理方式是,在很长一段时间里,只要可以保证向后兼容,那么只要可以使api变得更好,就可以被允许修改。

api的必要性

最容易维护的api就是没有api,因此,对我们开发的api的每一个方法和类都需要进行必要性的论证。在我们设计api的过程中,我们需要经常问自己一些问题,是否确实需要,并且持续维护api并保证功能的可用性。

在内部系统中使用开发的api

做为api开发者我们必须仔细确认并保证api针对相关领域的问题确实有用。我们应当站在使用者的角度而不是我们自己的角度来看待我们开发的api.最好的方法是在内部系统中使用它。换句话说,必须保证我们开发的api可以得到现实用户的信任。
得到现实用户使用的价值在于防止我们失去对api的约束,并且增加对api的理解。真实用户可以帮助平衡需求,以确保我们在做有价值的修复工作。当我们使用自己的api开发应用时,我们或许会看到使用api的这段代码并不整洁(或者意图不明确),取代那些重复的冗余代码,同时api会强迫我们抽象出一个合适的层来使用它。

api文档

有两种开发文档可以帮助开发者使用SDK: JavaDoc、更深度的使用文章(包含如果开始使用的教程等,比如微软发布的Java On Azure)。这些对开发者都很重要,但是他们是为不同的目地所服务的。这里主要介绍JavaDoc,这个和api开发者的关联的更加紧密。
JavaDoc 是api的规格说明。作为api开发者应当把写JavaDoc作为自己工作的一部分,并且确保完成,它包括类和方法的概述,明确输入、输出、异常情况和一些其他的细节。当这个文档作为规格说明,它不仅可以引导编程者,而且讨论该如何具体的实施api。
理想的情况是,更进一步去创建一个高质量的JavaDoc还可以包含代码片段以是用户可以在他们的项目中直接copy/paste,并使其开始工作。这些代码并不需要很长,最好可以限制在5-10行。随着时间的推移,当用户对api提出疑问时,这些代码可以逐渐添加到JavaDoc的类或者方法中。
JavaDoc的价值不仅在于提供给其他开发人员,同时可以帮助我们自己。这是因为JavaDoc通过对外部展示API的同时也会给我们一个SDK的面貌。
如果有一个定期生成JavaDoc的程序,那么在review我们的api的同时或许会发现JavaDoc中缺少的实现类或者外部依赖,或者其他不符合我们预期的事情。
大多数java项目基于Mavan或者Gradle,为一个项目生成JavaDocs通常可以使用mvc javadoc:javadoc或者gradle javadoc
养成定期生成文档的习惯(尤其是当配置异常出现error和warning的时候)可以确保我们及时发现api的问题,并且提醒我们的api需要更多的位置用来写JavaDoc

用于行为合同的JavaDoc

JavaDoc另一个未被使用的作用包括对指定行为的约束。一个栗子比如Arrays.sort()方法,是具有稳定性的特性(也就是说,相同元素不会被重新排序)。api没有可以简单表明(除了使我们的api显得笨重,比如Arrays.stableSort()),但是JavaDoc是一个理想的位置。

JavaDoc 标签

JavaDoc使用一些标签,比如@link,@param和@return,他们可以在html输出中生成。非常有用的是他们可以作为你的想法的备份。

风格的一致性

目前很少项目是由一个人开发完成的。人们的行为也是变化无常的,因此获取会导致一个接一个的错误。幸运的是,我们设计api时,可以做一个对公共api目标的明确的记录,当他偏离了这个目标时,可以轻松的发现。
拥有一个一致性的api的短期好处是可以增加用户的满意度,长期的好处是当用户使用api的新部分是,更容易知道如何使用它
一致性包括:

  • 返回类型:理想情况下,返回一个集合是需要被考虑的,包括少数几个类,而不是所有可能的类。一个好的集合返回类型可能包含,List,SetIterator(避免使用Collection,Iterable和Stream)。同时,如果api大多数情况下不会返回null,那么不要为其指定null的返回类型。
  • 方法名称模板:开发者可以使用IDE自动完成输入,所以尽可能开率api名称的重要性,确保IDE可以自动弹出包含名称的列表。API应该有一套完整的和可重复使用的术语表:类型名称,方法名称,参数名称,和常量等。
    方法名称比如 Type.of(...),Type.valueOf(),Type.toXYZ(),Type.from(...)等,应该持续使用,而不被混淆。我们的目标是在开发api时,是整个团队遵循一套命名规则。
  • 参数顺序:如果重载的方法包含多个参数,尽量保证参数顺序具备逻辑性。一些情况下,可以引入参数的包装类,这样可以降低后续版本增加更多参数的需求,同时增加api的可维护性。

最小化api

开发人员本能想写大而全的api,以提供更多的便利性。但是会有两个问题:

  1. api使用者需要了解超过他们实际工作所需要的信息
  2. 暴露的接口越多,后续需要维护的工作就越多
    所有api开发者应该设计并理解使用其api的关键用例,应该反对增加过多的用例(增加自己想象中的用来减少使用者代码的方法)。
    应该确定哪些是真正有价值的接口,同时放弃自认为有价值的方法。
    需要澄清的是,如果可以为使用者提供更加清晰便利的使用API,那便可以增加。比如JDK中的List.add(Object)方法,他避免开发者经常调用List.add(int,Object)
    Oracle的JDK团队的工程师Stuart Marks提供了一下见解:
    比如api中有两个方法bar()和foo(),如果使用者想一起使用,那么可以增加方法bf(),接下来增加一个新方法mumble(),那么三个综合起来的方法是fbm(),但是如果只使用bar()和mumble()呢,那么要新增一个方法bm(),这样下去,会造成一个臃肿的api(),远远超过基本的操作。
    在JDK的风格中,会让用户去组合使用api中基础的操作。但是其他第三方工具会提供一些基本操作的组合方法,比如Eclipse COllections。

防止泄露

确保一些具体的实现类以及api所依赖的类不会被泄露到public api中,有一些方法可以隐藏这些类:

  1. 将实现类放在 impl包里,这个包可以被JavaDoc排除掉。
  2. 将实现类设置为"package-private",可以确保这些类不是public API,因此不可以被使用者调用。

protected、final等关键字

protected关键字表示可以和子类交互,如果方法是私有的那么不应该声明为protected或public。
如果不希望使用者重写类和方法,那么可以使用final关键字来修饰。
一些情况下,使用者希望重写方法来满足他们特定的需求或者问题,这时候他们就会与我们进行联系。那么在下个版本,我们或许会移除掉final关键字。

保证向后兼容性

关于如何发展api的问题,基础的建议是如果需要修改,那么便新增一个,而不是修改或者删除一个已存在的api。原因是新增一个api可以保证向后兼容。如果对一个已经存在api进行修改或删除,会对用户造成风险,当他们更新api的版本后,会发现之前的代码不可用。
如果有一个向后无法兼容的改变,比如我们之前的api设计错误,或者在某些方面需要另外一种实现。那么我们就会遇到和用户进行沟通的挑战。使用@Deprecated注解 是一个好的方式,但是我们需要定义一个版本政策,比如新增一个新的版本(
软件版本控制规范可以参考,包括MAJOR.MINOR.PATCH版本),基于软件版本控制规范,所有需要修改或者变更的都可以标注deprecated,直到下以个MAJOR版本发布时删除。这个方法的重点在于确保使用者了解该api的遵从的版本规范。
另外,有时一些改动连开发者也没有注意到,那么一个工具Revapi可以起到帮助,他可以提示出api做了哪些无法向后兼容的改变。

不要返回null

通产在程序遇到异常时会返回null值,但一些情况下可以有更好的替代选择:

RETURN TYPENON-NULL RETURN VALUE
String“” (An empty string)
List / Set Map / IteratorUse the Collections class, e.g. Collections.emptyList()
StreamStream.empty()
ArrayReturn an empty, zero-length array
All other typesConsider using Optional (but read the Optional section below first)

使用以上规则替代null值,可以减少api使用者对null值的判断.

理解Optional

使用Optional可以减少空指针异常的可能性。如果方法返回Optional,便代表他是non-null的,换句话说Opthional<T>可以认为返回值至少包含一个元素。

  • 在使用optinnal返回值是,避免再返回null值。
  • 避免使用optional<Collection<T>>,简单使用Collection<T>做为返回值即可。

结论

本文档涵盖了一些在开发公共api时需要注意的事项。成功的api设计不在乎写了多少代码,而在于用户在其中获得了多少价值。因此一个精简的,风格一致的api是需要考虑的。 重要的是我们应当站在使用者的角度来使我们的api更加符合用户的真实需求。当然,
这不应该仅仅被视为“更多的工作”,而是对我们自己的挑战,以为用户创造一个方便使用,功能高效的API的目标而努力。

原文:Java API Best Practices

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/qingfengilp/article/details/84205983
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2021-12-11 17:47:51
  • 阅读 ( 645 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢