4. 集成接口定义与规范
接口,说白了就是你和SDK之间的合同。合同写清楚了,双方各司其职,集成就顺畅。合同写得含糊,后面就是无穷无尽的扯皮。我做了这么多年SDK集成,遇到过最头疼的问题,十有八九都出在接口定义不清晰上。
这一章我们就聊聊怎么定义好这份合同。不是泛泛讲理论,是实实在在能落地的规范。
核心思想:接口规范不是为了限制你,而是为了保护你。好的接口定义让集成方一看就懂,让SDK内部改起来也放心。
4.1 接口分类与设计原则
先搞清楚SDK对外暴露几类接口。我个人习惯分成三种:
| 接口类型 | 典型场景 | 适用模式 |
|---|---|---|
| 功能接口 | 初始化、登录、支付、上报 | 同步请求 / 异步回调 |
| 事件接口 | 用户状态变化、网络切换、静默升级 | 回调 / 通知 / 观察者 |
| 配置接口 | 设置阈值、开关、缓存策略 | 简单属性设置 |
设计原则我总结了三条:
- 单一职责:一个接口只做一件事。别把初始化、登录、拉配置塞到一个方法里。
- 显式声明:参数必须标明是否可为空、类型的取值范围。我见过一个接口传int做flag,文档只写“0-正常 1-异常”,结果集成方传了2,SDK直接崩了。
- 向后兼容:新增参数必须给默认值,绝不能删老参数。这个后面细说。
一个小技巧:接口命名尽量用动词+名词,例如startLogUpload、registerPushListener。这样集成方扫一眼就知道干啥用的。
4.2 接口文档规范
文档不是写给自己看的,是写给集成方看的。我要求团队里的文档必须包含下面这些要素:
- 签名与描述:方法名、所属类、一句话用途
- 参数表:参数名、类型、必填/可选、说明、取值范围、默认值
- 返回值/回调:返回类型、成功/失败时的数据结构
- 错误码:每个错误码的含义、可能原因、建议处理方式
- 调用示例:伪代码或真实代码片段,至少覆盖主流场景
举个例子,一个初始化接口的文档片段:
/**
* 初始化SDK
* @param {String} appId - 必须,应用唯一标识
* @param {String} appSecret - 必须,应用密钥
* @param {int} timeout - 可选,超时时间(毫秒),默认3000
* @param {Function} onResult - 回调,返回 { code: int, message: String }
* @returns {boolean} - 同步返回是否调用成功(不代表初始化完成)
*/
SDK.init(appId, appSecret, timeout, onResult);
你想想看,集成方拿到这个文档,直接就能写代码了。不需要追着你问“超时传什么单位啊?”“回调里的code哪些是成功哪些是失败?”
踩坑提醒:我曾经在文档里只写了“错误码请参考附录”,结果附录是另一个团队维护的内部wiki,集成方永远找不到。后来我强制要求每个接口的错误码单独列出来,哪怕重复也要列。
4.3 版本控制与兼容性
SDK迭代是常态。但每次升级怎么通知集成方?怎么保证老的集成代码还能用?这件事上我吃过不少亏。
我推荐采用语义化版本控制(SemVer):
| 版本号 | 含义 | 集成方应对策略 |
|---|---|---|
| MAJOR(主版本) | 不兼容的API修改 | 必须修改代码,重新测试 |
| MINOR(次版本) | 向下兼容的新功能 | 安全升级,可选择性使用新功能 |
| PATCH(修订号) | 向下兼容的问题修复 | 放心升级,无可见影响 |
实际操作中,有个细节容易被忽略:回调接口的签名变更。你新增一个回调参数,老版本没传这个参数,SDK直接传undefined?还是给默认值?我个人认为,所有回调参数必须提供默认值,并且在新版本中保持旧参数顺序不变。如果非要新增参数,只能在末尾追加。
一条铁律:永远不要删除或重命名已经发布的接口、参数或枚举值。如果实在要废弃,用@deprecated标记,至少保留两个主版本。
嗯,这里还要提一下接口的废弃策略。我习惯的做法是:
- 在文档中标记
@deprecated,并注明替代接口。 - 代码运行时打印警告日志(但不要抛异常)。
- 两个主版本后,正式移除。
这样集成方有充足的时间迁移。
4.4 错误处理与异常规范
接口调用的结果,要么成功,要么失败。但失败的表达方式千差万别。有些SDK用返回null表示失败,有些用异常,有些用错误码。我最怕的是混用——同一个接口,某些场景抛异常,某些场景返回错误码。
我建议统一用一种模式:
- 同步接口:使用返回值或out参数传递结果,错误通过错误码返回,不允许抛检查型异常。
- 异步接口:通过回调传递错误,回调参数包含
{ code, message, data }。
错误码的设计也有讲究。我遇到过最蠢的设计:错误码用负整数,0表示成功,其他都表示失败,但文档里只列了20个,实际SDK内部可能返回50个。集成方根本不知道碰到-999该咋办。
我的建议:错误码采用模块前缀+数字,例如 NET_001、AUTH_002。这样一看就知道是哪个模块出的问题。同时务必提供一个“未知错误”的兜底码,保证所有异常都有码可查。
4.5 接口定稿与评审
接口定义完成后,不要急着写代码。我要求团队成员先出接口文档,然后拉集成方(也就是使用SDK的客户端团队)一起走一遍评审。评审时重点看:
- 接口命名是否直观
- 参数顺序是否符合直觉(通常把必填参数放前面)
- 回调时机是否明确(比如是在主线程还是子线程)
- 边界条件是否覆盖(参数为null、空字符串、超大值时的表现)
评审通过后,接口文档必须锁定版本,后续修改必须走变更流程。说白了,接口就是SDK的外立面,拆了重建代价极大。前期多花点时间定义清楚,后面能少加无数个班。
血泪教训:我曾经跳过评审,直接按自己的想法定义了一组接口。结果集成方说“你这个回调在主线程,我还得切线程” “你这个参数名initData,到底传JSON还是对象啊?” 最后光改接口就改了三版,比直接写文档多花了三倍的时间。从那以后,评审成了硬门槛。
4.6 接口规范核心要素总览
下面这张图概括了接口规范涉及的主要模块和它们之间的关系。你可以把它当作集成接口规范的Checklist。
这张图从顶层的接口合约出发,列出了定义接口必须关注的五个维度,以及每个维度下的具体规范要求。最终通过文档和评审,输出一份可交付的接口规范。
记住,接口规范不是做完就完了,它需要随SDK一起迭代维护。每次版本变更,都要同步更新接口文档和错误码表。你想想看,如果集成方拿到的文档跟实际SDK行为对不上,信任感瞬间就没了。
好了,这一章就到这里。接口定义这件事,说难也不难,就是需要你多站在集成方的角度想一想。把丑话说在前头,把规范写清楚,后面大家都省心。