SDK 架构设计:分层、模块化与接口原则

做 SDK 开发这么多年,我踩过最大的坑就是架构设计。早期接手过一个项目,所有逻辑都摊在一个类里,业务方一接入就崩溃。说白了,SDK 这东西,它是跑在别人 App 里的代码。你的代码不干净,人家的应用就会卡死。

我习惯用 分层架构 + 模块化拆分 + 稳定接口 这套组合拳。下面聊点实实在在的体会。

1. 分层架构:把代码切三块

为什么要分层?你想想看,如果网络层和 UI 层混在一起,换个网络库就得重写半个 SDK。

我一般分成三层:
接入层(Api)—— 对外暴露的 API。
核心层(Core)—— 业务逻辑和状态管理。
基础层(Foundation)—— 网络、存储、日志。

层次 职责 依赖关系
接入层 协议校验、参数检查、回调分发 只依赖核心层接口
核心层 业务编排、状态机、调度逻辑 只依赖基础层接口
基础层 DNS 解析、连接池、文件 IO 不依赖上层

关键原则:依赖单向流动。下层绝不能反过来调用上层。我在项目中见过有人直接在上层回调里操作数据库,最后加了两层抽象才解耦合。

2. 模块化设计:解耦到极致

分层是纵向切,模块化是横向切。每个模块只关心一件事。

我常把 SDK 拆成下面几个模块:

  • 初始化模块:配置加载、依赖检查、组件启动顺序。
  • 网络模块:请求封装、重试策略、超时控制。
  • 存储模块:缓存策略、数据加密、本地持久化。
  • 事件总线:异步消息分发、生命周期管理。
  • 日志模块:分级输出、日志回捞、性能打点。

举个代码结构例子:

sdk/
├── api/               # 接入层,对外暴露
│   ├── SdkApi.java
│   └── SdkConfig.java
├── core/              # 核心层,内部逻辑
│   ├── init/
│   │   └── InitPipeline.java
│   ├── network/
│   │   └── RequestDispatcher.java
│   └── storage/
│       └── CacheManager.java
└── foundation/        # 基础层,工具集合
    ├── http/
    │   └── HttpClient.java
    ├── log/
    │   └── Logger.java
    └── util/
        └── ThreadPoolProvider.java

我的经验:模块之间只通过接口通信。举个例子,网络模块不直接用 OkHttp,而是定义 IHttpClient 接口。这样换网络库只需加一个实现类。我曾经用这个办法让 SDK 无缝切换了三次底层网络库。

3. 接口设计原则:稳如磐石

接口就是 SDK 的契约。一旦发布,就不能随便改。我这里总结了三字诀。

3.1 稳定性 > 灵活性

尽量用 Builder 模式替代多个重载方法。参数特别多时,config.setTimeout(5000) 比 init(a,b,c,d,e) 优雅得多。我在项目中遇到过接口参数从 5 个涨到 15 个,最后不得不废弃旧接口。改成 Builder 后再也没出过这问题。

3.2 收敛暴露面

只暴露最少必要的类。内部实现类全部 包级私有 或加 @Internal 注解。业务方看到你的 API 文档只有 5 个类,心里就有底。

3.3 异步回调必须规范

所有回调回到 主线程。回调参数用 Result<T> 封装成功和失败,别让使用者去判断 null。

// 推荐的做法
public class SdkResult<T> {
    private T data;
    private int errorCode;
    private String errorMsg;

    public boolean isSuccess() { return errorCode == 0; }
    public T getData() { return data; }
}

// 使用示例
sdk.fetchData(new SdkCallback<SdkResult<UserInfo>>() {
    @Override
    public void onResult(SdkResult<UserInfo> result) {
        if (result.isSuccess()) {
            // 处理成功
        } else {
            // 处理失败
        }
    }
});

3.4 版本兼容

废弃接口标注 @Deprecated,但至少保留两个大版本才移除。我不建议直接删掉方法,可以改成空实现或者抛 UnsupportedOperationException。

避坑指南:我曾经在接口里返回了 HashMap,结果业务方直接 put 了数据,导致内部共享状态全乱套。后来改成 Collections.unmodifiableMap() 才解决。

SDK 分层架构全景图 接入层(Api) • 参数校验 • 权限检查 • 线程切换 核心层(Core) • 状态机 • 业务编排 • 模块调度 基础层(Foundation) • 网络、存储、日志 • 线程池、工具类 网络模块 存储模块 初始化模块 日志模块 事件总线 设计原则 依赖单向流动 模块间通过接口通信

画完这张图你就明白了。左边是纵向分层,右边是横向模块。每一层都通过稳定接口与相邻层交互。模块之间彼此独立,模块内部的高内聚和模块间的低耦合做到位了,SDK 就稳了。

最后说两句

架构设计没有银弹。但分层、模块化、接口稳定这套组合拳,我用了六年,在十几个商业 SDK 里验证过。项目初期多花两天设计好骨架,后期能省两个月修 bug 的时间。这个账,一定要算清楚。