社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近在做接口对接的工作,发现要写出一个双方都满意的api并不是一件容易的事,刚好在DZone 上看到微软工程师 Jonathan Giles 写的一篇文章,学到了一些经验,便做了翻译或许可以帮助到更多的开发者。
作为一个开发者,我们工作就是每天写代码,当然我们不可以脱离于其他人而写代码。可以确定的是我们都在踩在前人的肩膀上进行学习工作,今天有大量的工具可以供我们使用,
比如github,stack overflow,maven central,还有很多可用的第三方库可以供我们使用,软件是在api(Application Programming Interfaces)基础上创造的,我们使用大量来自Maven或者Gradle的第三方库。
如果我们询问工程师们他们是不是API开发者,他们通常会回答不是,但是这个回答有不正确的,工程师只要曾经精心写出过public的类或者public的方法,就可以被看作是api开发者。
“精心”这个形容词是必要的,API的设计就像一件艺术品,需要创造力并且需要付出很时间,同时更像是一门科学。
API风格有很多衡量的标准,下面介绍6点标准。
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会强迫我们抽象出一个合适的层来使用它。
有两种开发文档可以帮助开发者使用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另一个未被使用的作用包括对指定行为的约束。一个栗子比如Arrays.sort()
方法,是具有稳定性的特性(也就是说,相同元素不会被重新排序)。api没有可以简单表明(除了使我们的api显得笨重,比如Arrays.stableSort())
,但是JavaDoc是一个理想的位置。
JavaDoc使用一些标签,比如@link,@param和@return,他们可以在html输出中生成。非常有用的是他们可以作为你的想法的备份。
目前很少项目是由一个人开发完成的。人们的行为也是变化无常的,因此获取会导致一个接一个的错误。幸运的是,我们设计api时,可以做一个对公共api目标的明确的记录,当他偏离了这个目标时,可以轻松的发现。
拥有一个一致性的api的短期好处是可以增加用户的满意度,长期的好处是当用户使用api的新部分是,更容易知道如何使用它
一致性包括:
List,Set
和Iterator
(避免使用Collection,Iterable和Stream)。同时,如果api大多数情况下不会返回null,那么不要为其指定null的返回类型。Type.of(...),Type.valueOf(),Type.toXYZ(),Type.from(...)
等,应该持续使用,而不被混淆。我们的目标是在开发api时,是整个团队遵循一套命名规则。开发人员本能想写大而全的api,以提供更多的便利性。但是会有两个问题:
确保一些具体的实现类以及api所依赖的类不会被泄露到public api中,有一些方法可以隐藏这些类:
impl
包里,这个包可以被JavaDoc排除掉。protected关键字表示可以和子类交互,如果方法是私有的那么不应该声明为protected或public。
如果不希望使用者重写类和方法,那么可以使用final关键字来修饰。
一些情况下,使用者希望重写方法来满足他们特定的需求或者问题,这时候他们就会与我们进行联系。那么在下个版本,我们或许会移除掉final关键字。
关于如何发展api的问题,基础的建议是如果需要修改,那么便新增一个,而不是修改或者删除一个已存在的api。原因是新增一个api可以保证向后兼容。如果对一个已经存在api进行修改或删除,会对用户造成风险,当他们更新api的版本后,会发现之前的代码不可用。
如果有一个向后无法兼容的改变,比如我们之前的api设计错误,或者在某些方面需要另外一种实现。那么我们就会遇到和用户进行沟通的挑战。使用@Deprecated
注解 是一个好的方式,但是我们需要定义一个版本政策,比如新增一个新的版本(软件版本控制规范可以参考,包括MAJOR.MINOR.PATCH版本),基于软件版本控制规范,所有需要修改或者变更的都可以标注deprecated,直到下以个MAJOR版本发布时删除。这个方法的重点在于确保使用者了解该api的遵从的版本规范。
另外,有时一些改动连开发者也没有注意到,那么一个工具Revapi可以起到帮助,他可以提示出api做了哪些无法向后兼容的改变。
通产在程序遇到异常时会返回null值,但一些情况下可以有更好的替代选择:
RETURN TYPE | NON-NULL RETURN VALUE |
---|---|
String | “” (An empty string) |
List / Set Map / Iterator | Use the Collections class, e.g. Collections.emptyList() |
Stream | Stream.empty() |
Array | Return an empty, zero-length array |
All other types | Consider using Optional (but read the Optional section below first) |
使用以上规则替代null值,可以减少api使用者对null值的判断.
使用Optional可以减少空指针异常的可能性。如果方法返回Optional,便代表他是non-null的,换句话说Opthional<T>
可以认为返回值至少包含一个元素。
optional<Collection<T>>
,简单使用Collection<T>
做为返回值即可。本文档涵盖了一些在开发公共api时需要注意的事项。成功的api设计不在乎写了多少代码,而在于用户在其中获得了多少价值。因此一个精简的,风格一致的api是需要考虑的。 重要的是我们应当站在使用者的角度来使我们的api更加符合用户的真实需求。当然,
这不应该仅仅被视为“更多的工作”,而是对我们自己的挑战,以为用户创造一个方便使用,功能高效的API的目标而努力。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!