面对 10 个以上模块依赖的核心模块,API 设计已经超出了“写代码”的范畴,进入了**“制定标准”的领域。核心模块的任何变动都会引发巨大的“扇出效应”(Fan-out Effect)**,导致上游编译时间爆炸或运行时行为异常。
要保持 API 稳定,需要构建一套**防御式、可扩展且具备“版本兼容性”**的体系。
1. 最小表面积原则(Minimal Surface Area)
“API 越少,你欠的债就越少。”
-
隐藏实现细节:将所有内部辅助类、扩展方法设为
internal。只暴露必要的协议和结构体。 -
不透明类型(Opaque Types) :利用
some或any关键字隐藏具体返回类型。- 好处:即使你后续更换了内部的算法或存储引擎,只要接口满足协议,依赖方就不需要任何改动。
-
强类型枚举而非 String:防止依赖方传递非法参数,将错误拦截在编译期。
2. 利用“版本化代理”处理演进
当一个 API 必须改变时,不要直接修改原函数,而是采用**“标记 -> 新增 -> 迁移”**的三部曲。
-
使用
@available降级:Swift
@available(*, deprecated, renamed: "newMethod(with:)", message: "此 API 将在 v2.0 移除") public func oldMethod() { ... } -
参数包装器(Options Pattern) :
如果函数参数可能增加,不要直接加参数。改用一个
Configuration结构体。Swift
// ❌ 不利于扩展 func setup(id: String, theme: Color) // ✅ 极具弹性 struct SetupOptions { let id: String var theme: Color = .blue var cachePolicy: CachePolicy = .default // 新增字段不会破坏旧调用 } func setup(with options: SetupOptions)
3. 依赖反转:接口模块化(Interface Segregation)
如果核心模块直接持有大量逻辑,它的体积会迅速膨胀。
-
提取契约层(Contract Layer) :
创建一个只包含 Protocol 和 Entity 的轻量级 Target(例如
CoreInterface)。- 10 个模块只依赖
CoreInterface,而不依赖CoreImplementation。 - 结果:你可以在不触发上游重编的情况下,随意重构核心模块的实现细节。
- 10 个模块只依赖
4. 稳定性防护:二元分发策略
针对核心模块,建议采用以下物理隔离手段:
| 策略 | 做法 | 目的 |
|---|---|---|
| 二进制分发 (XCFramework) | 将核心模块编译为二进制。 | 物理上禁止依赖方修改代码,显著缩短上游模块编译耗时。 |
| API Snapshot 测试 | 使用工具记录 API 的 Public 签名。 | 在 CI/CD 阶段自动检测是否有非预期的 API 变更。 |
| 功能开关 (Feature Flags) | API 逻辑内部内置开关。 | 允许新逻辑静默上线,一旦出 Bug 可快速回滚而不必重新发版。 |
5. 容错设计:防腐层(Anticorruption Layer)
核心模块的 API 应该像“堡垒”一样。
- 严格的输入校验:不要相信依赖方传进来的数据。在 API 入口处进行合法性检查。
- 错误类型化:提供详尽的
Error枚举,让依赖方知道是因为“配置错误”还是“网络超时”,而不是给一个模糊的NSError。