一、设计原则
-
最小接口原则(Interface Segregation)
- 提供尽量精简、单一职责的 API
- 不暴露内部实现细节
- 依赖方只看到它需要的功能
-
稳定性优先
- 依赖方越多,API 越难改动
- 尽量保持向后兼容,避免破坏依赖链
-
抽象化和协议化
- 依赖模块面向协议而非具体实现
- 保持可替换性和测试能力
-
单向依赖
- 模块不依赖使用它的模块
- API 不泄露依赖方上下文
二、API 设计策略
1️⃣ 封装核心能力,隐藏实现
- 只暴露必需方法 / 类型
- 内部实现细节用
internal/ private / target-specific hidden
public protocol AuthService {
func login(username: String, password: String) async throws
func logout() async
}
- 使用模块的 10 个模块只看到 AuthService 协议
- 内部实现可随意优化
2️⃣ 分层 API,提供组合入口
- 如果模块功能复杂,拆分成 子协议 / 小接口
- 避免单个接口过大,依赖方只引入需要的部分
public protocol UserRepository {
func fetchUser(id: String) async -> User
}
public protocol UserUpdater {
func updateUser(_ user: User) async
}
- 不同依赖方只依赖
UserRepository或UserUpdater
3️⃣ 保持不可变 / 纯函数接口优先
- 避免依赖方修改内部状态
- 提供返回新实例或异步任务,而不是可变共享对象
func filteredUsers(role: Role) -> [User]
- 保持线程安全、可预测性
4️⃣ 异步 / 副作用通过 Effect / Publisher 暴露
- 避免模块直接操作依赖方 UI / 状态
- 使用 Combine / async/await / callback 返回结果
func observeLoginState() -> AnyPublisher<LoginState, Never>
- 模块只负责业务逻辑
- 依赖方根据状态做 UI / 逻辑处理
5️⃣ 版本管理与向后兼容
-
被多模块依赖 → API 升级必须小心
-
建议:
- 使用默认参数 / protocol extension 保持旧 API
- 通过 deprecate 标记引导迁移
三、工程实践建议
| 方向 | 方法 | 目的 |
|---|---|---|
| 可替换性 | 面向协议 / 抽象接口 | 依赖方可以替换实现,测试容易 |
| 隐藏内部 | internal / fileprivate / private | 避免依赖方误用内部实现 |
| 可扩展 | 小接口 + 默认实现 | 方便未来增加功能,不破坏旧依赖 |
| 状态安全 | 返回值 / Publisher / async | 避免共享可变状态 |
| 版本安全 | deprecate / default arg | 避免 10 个模块同时改动 → 崩盘 |
四、核心结论
API 设计的目标 = 最小、稳定、抽象、可预测
物理上被 10 个模块依赖,不是“多点调用”的问题,而是 每个依赖方只看到它需要的协议和能力,不暴露内部状态和实现。