2、SDK架构设计原则:高内聚低耦合、接口最小化原则、版本兼容性设计、依赖管理策略

好,咱们直接进入正题。SDK 的架构设计,说白了就是一场「平衡的艺术」。你既要让功能足够强大,又要让接入方觉得「嗯,这东西真清爽」。我做了这么多年 SDK,踩过的坑比写过的代码还多。今天聊的这四个原则,每一个都是用真金白银的线上事故换来的。

2.1 高内聚低耦合:别让模块之间「拉拉扯扯」

什么叫高内聚?就是一个模块只干一件事,而且干得漂亮。什么叫低耦合?就是模块之间尽量别互相依赖,最好老死不相往来。

我见过最离谱的 SDK,登录模块里居然调用了支付模块的接口。为什么?因为当初写代码的人图省事,想复用支付模块里的一个工具函数。结果呢?后来支付模块重构了,登录模块跟着崩了。你说冤不冤?

核心原则: 每个模块的职责边界要清晰。登录就是登录,支付就是支付,别混在一起。

具体怎么做?我建议你遵循这几点:

  • 模块只暴露必要接口:内部实现细节全部隐藏。能用 @interface 就别用 @public。
  • 依赖方向要单向:上层模块可以依赖下层模块,但下层模块绝不能反向依赖上层。
  • 用事件总线解耦:模块之间需要通信?用事件通知,别直接调用对方的方法。

举个例子,我之前重构过一个推送 SDK。原来的代码里,网络模块和 UI 模块直接耦合在一起。网络一断,UI 直接卡死。后来我把网络状态变化改成事件广播,UI 模块只监听事件,再也不直接调用网络模块了。问题瞬间解决。

小技巧: 写代码前先画依赖图。如果发现循环依赖,说明你的架构设计有问题,赶紧改。

2.2 接口最小化原则:少即是多

这个原则我体会最深。很多 SDK 开发者有个毛病——恨不得把所有功能都暴露出去。结果呢?接入方看着几十个 API 接口,一脸懵逼。

接口最小化原则,说白了就是:只暴露对方真正需要的。能用一个接口解决的问题,绝不用两个。

我自己的习惯是:

  1. 先问自己三个问题:这个接口真的需要吗?有没有更简单的方式?对方能不能理解?
  2. 接口参数越少越好:能设默认值的就别让对方传。我记得有个项目,初始化接口有 12 个参数,后来我砍到只剩 3 个,接入时间从 2 天缩短到 2 小时。
  3. 用 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 里塞满了各种依赖,接入方一看就头大。

我的原则很简单:能不依赖就不依赖,能少依赖就少依赖。

具体策略:

  1. 优先使用系统 API:系统自带的 API 能解决 80% 的问题。别动不动就引入第三方库。
  2. 依赖隔离:如果必须依赖第三方库,把它封装在你的模块内部,不要暴露给接入方。
  3. 使用依赖注入:让接入方自己决定用什么依赖,而不是你替他们决定。
举个例子: 我之前做一个图片加载 SDK,本来想依赖 Glide。但后来发现,很多接入方已经在用 Picasso 了。怎么办?我干脆做了一个抽象层,让接入方自己选择图片加载库。这样既灵活又避免了依赖冲突。

依赖管理的另一个重点是版本锁定。你的 SDK 依赖了某个库的 1.0 版本,但接入方可能用了 2.0 版本。这时候就会出问题。我的做法是:在发布 SDK 时,明确声明依赖的版本范围,并且用 provided 或 compileOnly 来避免打包冲突。

注意: 依赖冲突是 SDK 开发里最常见的线上问题之一。一定要在发布前做好依赖树分析,确保没有版本冲突。

2.5 知识体系总览

下面这张图,是我对这四个原则的总结。你可以把它当作一个「检查清单」,每次设计 SDK 架构时,对照着看看自己有没有遗漏。

SDK架构设计四大原则 高内聚低耦合 • 模块职责单一,边界清晰 • 依赖方向单向,避免循环 • 事件总线解耦模块通信 接口最小化原则 • 只暴露真正需要的接口 • 参数越少越好,设默认值 • Builder模式替代长参数 版本兼容性设计 • 语义化版本号管理 • 废弃接口保留两个大版本 • 版本适配层「换芯不换壳」 依赖管理策略 • 能不依赖就不依赖 • 依赖隔离,不暴露给接入方 • 版本锁定,避免冲突 平衡的艺术:功能强大 vs 接入简单

这四个原则,说起来简单,做起来难。但只要你每次设计 SDK 架构时,都拿这张图对照一下,我相信你能少踩很多坑。嗯,今天就聊到这里,希望对你有所帮助。

可可精品资料库