2. SDK架构设计原则
好,咱们直接进入正题。SDK 的架构设计,说白了就是一场「平衡的艺术」。你既要让功能足够强大,又要让接入方觉得「嗯,这东西真清爽」。我做了这么多年 SDK,踩过的坑比写过的代码还多。今天聊的这四个原则,每一个都是用真金白银的线上事故换来的。
2.1 高内聚低耦合:别让模块之间「拉拉扯扯」
什么叫高内聚?就是一个模块只干一件事,而且干得漂亮。什么叫低耦合?就是模块之间尽量别互相依赖,最好老死不相往来。
我见过最离谱的 SDK,登录模块里居然调用了支付模块的接口。为什么?因为当初写代码的人图省事,想复用支付模块里的一个工具函数。结果呢?后来支付模块重构了,登录模块跟着崩了。你说冤不冤?
具体怎么做?我建议你遵循这几点:
- 模块只暴露必要接口:内部实现细节全部隐藏。能用
@interface就别用@public。 - 依赖方向要单向:上层模块可以依赖下层模块,但下层模块绝不能反向依赖上层。
- 用事件总线解耦:模块之间需要通信?用事件通知,别直接调用对方的方法。
举个例子,我之前重构过一个推送 SDK。原来的代码里,网络模块和 UI 模块直接耦合在一起。网络一断,UI 直接卡死。后来我把网络状态变化改成事件广播,UI 模块只监听事件,再也不直接调用网络模块了。问题瞬间解决。
2.2 接口最小化原则:少即是多
这个原则我体会最深。很多 SDK 开发者有个毛病——恨不得把所有功能都暴露出去。结果呢?接入方看着几十个 API 接口,一脸懵逼。
接口最小化原则,说白了就是:只暴露对方真正需要的。能用一个接口解决的问题,绝不用两个。
我自己的习惯是:
- 先问自己三个问题:这个接口真的需要吗?有没有更简单的方式?对方能不能理解?
- 接口参数越少越好:能设默认值的就别让对方传。我记得有个项目,初始化接口有 12 个参数,后来我砍到只剩 3 个,接入时间从 2 天缩短到 2 小时。
- 用 Builder 模式替代长参数列表:参数太多?用 Builder 模式,既清晰又灵活。
来看一个对比:
| 反面案例 | 正面案例 |
|---|---|
SDK.init(appId, appKey, channelId, userId, userName, userAvatar, ...) |
SDK.init(config),其中 config 只包含必要字段 |
| 接口参数多达 10+ 个 | 接口参数不超过 5 个 |
| 接入方需要查阅大量文档 | 接入方 5 分钟就能搞定 |
2.3 版本兼容性设计:别让升级变成「灾难」
版本兼容性,是 SDK 开发里最头疼的问题之一。你想想看,你的 SDK 被几百个 App 集成,每个 App 用的版本都不一样。你升级一个版本,如果破坏了兼容性,那后果...嗯,我不敢想。
我曾经就吃过这个亏。有一次我重构了网络请求模块,把接口返回的数据结构改了。结果呢?线上直接炸了,因为老版本的接入方还在用旧的数据结构解析。那一次事故,让我明白了版本兼容性的重要性。
我的经验是:
- 语义化版本号:主版本号、次版本号、修订号,各司其职。主版本号变更表示不兼容,次版本号变更表示新增功能但兼容,修订号变更表示 bug 修复。
- 接口标记为 @Deprecated:要废弃一个接口?先标记为废弃,保留至少两个大版本周期再删除。
- 提供迁移指南:每次大版本升级,都要写清楚「从旧版本迁移到新版本需要做什么」。
另外,我建议你在 SDK 内部做一层「版本适配层」。这样即使底层实现变了,对外接口也能保持稳定。说白了,就是「换芯不换壳」。
2.4 依赖管理策略:别让依赖变成「噩梦」
依赖管理,是 SDK 开发里最容易出问题的地方。你依赖了一个第三方库,结果这个库又依赖了另一个库...最后你的 SDK 里塞满了各种依赖,接入方一看就头大。
我的原则很简单:能不依赖就不依赖,能少依赖就少依赖。
具体策略:
- 优先使用系统 API:系统自带的 API 能解决 80% 的问题。别动不动就引入第三方库。
- 依赖隔离:如果必须依赖第三方库,把它封装在你的模块内部,不要暴露给接入方。
- 使用依赖注入:让接入方自己决定用什么依赖,而不是你替他们决定。
依赖管理的另一个重点是版本锁定。你的 SDK 依赖了某个库的 1.0 版本,但接入方可能用了 2.0 版本。这时候就会出问题。我的做法是:在发布 SDK 时,明确声明依赖的版本范围,并且用 provided 或 compileOnly 来避免打包冲突。
2.5 知识体系总览
下面这张图,是我对这四个原则的总结。你可以把它当作一个「检查清单」,每次设计 SDK 架构时,对照着看看自己有没有遗漏。
这四个原则,说起来简单,做起来难。但只要你每次设计 SDK 架构时,都拿这张图对照一下,我相信你能少踩很多坑。嗯,今天就聊到这里,希望对你有所帮助。