2. SDK 架构设计原则
说实话,我见过太多 SDK 因为一开始没想清楚设计原则,后面改得面目全非。开发者骂,维护的人更痛苦。今天咱们把这五个原则掰开揉碎,每个我都踩过坑,希望能帮你少走弯路。
2.1 高内聚低耦合
先说说高内聚。说白了,就是把 同类功能关在一个屋子里,别到处乱跑。比如网络请求模块,就该只管网络相关的活儿——重试、超时、证书校验。千万别在里面塞一个 UI 弹窗,或者写个本地缓存逻辑。
低耦合呢,就是模块之间通过接口说话,别直接 new 对象。
判断标准其实很简单:能不能单独测试这个模块? 如果能,高内聚就到位了。
2.2 最小依赖
这个问题我当年栽过跟头。一个 SDK 引了十几个第三方库——Gson、OkHttp、RxJava、Guava……结果用户的应用因为依赖冲突,编译都过不了。
我的建议是:能自己写就别引入。真的需要用第三方,优先选 零依赖 的库。比如 JSON 解析,用官方的 org.json 就够了,不用非得上 Gson。
| 依赖原则 | 具体做法 | 避坑提示 |
|---|---|---|
| 只引入必要模块 | 按需导入,不打包全量 | 用 ProGuard/R8 做瘦身 |
| 版本统一 | 中央仓库声明依赖版本 | 别写 compileOnly 但运行时没有 |
| 避免传递依赖 | 用 exclude 或者 provided | 我记得有一次忘记 exclude 了,用户一把辛酸泪 |
2.3 易扩展
你想想看,业务永远是活的。今天要加个埋点,明天要改个认证方式。SDK 如果写死了,那就等着重构吧。
我用的核心模式是 策略模式 + 工厂模式。比如推送模块,接口定义好,具体实现(极光、个推、华为)通过 SPI 或者注入加载。新厂商来了,加一个类就行,核心代码不动。
// 扩展点示例:认证器接口
public interface Authenticator {
authenticate(Credential credential);
}
// 默认实现:密码认证
public class PasswordAuthenticator implements Authenticator { ... }
// 新需求:指纹认证,加个实现类就好
public class FingerprintAuthenticator implements Authenticator { ... }
OnConfigChangedListener。用户可能想监听 SDK 内部的配置变化,你不提供的代价就是他 hook 你的私有方法。
2.4 API 稳定性
API 是 SDK 和用户之间的契约。改了契约,用户就要改代码。我见过被迫升级 SDK 的团队,界面重写、数据迁移,加班到半夜。
我坚持两点:公开方法加 @since 注解,标明从哪个版本开始支持;标记废弃用 @Deprecated,并且至少保留三个小版本再删。
比如一个登录方法:
// 1.x 版本
public User login(String username, String password);
// 2.0 废弃,换成了对象参数
@Deprecated
public User login(String username, String password);
public User login(LoginRequest request);
嗯,这里要注意:不要偷偷改方法签名。参数类型变了,编译不过;返回值变了,运行时崩。都是大雷。
2.5 版本兼容性设计
SDK 版本怎么升?语义化版本号(SemVer)是标准做法:主版本.次版本.补丁。但光有号不行,还得有策略。
- 向前兼容:新版本 SDK 不能破坏旧版本 API。新增接口可以,改删不行。
- 向后兼容:旧 SDK 数据能被新版本正确解析。常见坑是序列化对象新增字段,旧版反序列化会抛异常。解决方法是
@SerializedName加默认值。 - 运行时兼容:比如 Android 不同 API Level 差异。别全量判断,用
Build.VERSION.SDK_INT分路径。
这里画张图,把这五个原则的关系串起来。
这五个原则其实层层递进。先保证内聚耦合,再控制依赖,接着预留扩展点,然后守住 API 稳定,最后用版本兼容性兜底。顺序别搞反了,否则后面会很难受。