使用协议抽象实现依赖倒置
以 Swift 协议作为边界,依赖抽象而非具体实现。
使用协议抽象实现依赖倒置 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。
依赖倒置原则
高层模块不应依赖低层模块。两者都应依赖抽象(协议)。
// WITHOUT DIP:
class OrderService { let db = SQLiteDatabase() } // depends on concrete
// WITH DIP:
class OrderService { let db: Database } // depends on abstraction定义抽象
提取一个协议,用于描述高层模块所需的契约。
protocol Database {
func save<T: Encodable>(_ value: T, key: String) throws
func load<T: Decodable>(key: String) throws -> T
}具体实现
在协议背后提供多个具体实现:生产实现、测试实现以及未来的替代实现。
class SQLiteDatabase: Database { ... }
class InMemoryDatabase: Database { ... }
class KeychainDatabase: Database { ... }构造器注入
通过初始化器传入抽象,这是最明确且最易测试的注入方式。
class ProfileService {
private let db: Database
init(db: Database) { self.db = db }
}为什么不把所有内容都做成单例
全局单例会隐藏依赖,使代码难以测试和理解。
// Hard to test:
class Service { func run() { MySingleton.shared.doWork() } }
// Easy to test:
class Service { let dep: MyProtocol; func run() { dep.doWork() } }Swift 中的协议抽象
Swift 协议可以携带关联类型、默认实现和组合能力,非常适合构建丰富的抽象。
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
func set(_ key: Key, value: Value)
}抽象粒度
接口隔离:相比臃肿的协议,应优先使用精简协议,使实现只需提供调用方所需的内容。
// Fat (avoid):
protocol DataStore {
func save(...); func load(...); func delete(...); func query(...); func migrate()
}
// Narrow (prefer):
protocol Readable { func load<T>(...) throws -> T }
protocol Writable { func save<T>(...) throws }使用 Combine 实现依赖倒置
让协议方法返回 AnyPublisher,从而隐藏底层发布者类型。
protocol UserStream {
func userUpdates() -> AnyPublisher<User, Never>
}使用 async/await 实现依赖倒置
标记为 async throws 的协议方法与 Swift 结构化并发完全兼容。
protocol Analytics {
func track(event: String) async
}
struct FirebaseAnalytics: Analytics {
func track(event: String) async { /* Firebase call */ }
}应用层的依赖倒置
在应用入口(组合根)装配具体实现,将所有绑定集中在一个位置。
@main
struct MyApp: App {
let db: Database = SQLiteDatabase()
let repo: UserRepository = RemoteUserRepository(db: db)
let useCase = FetchUserUseCase(repo: repo)
var body: some Scene {
WindowGroup { ContentView(useCase: useCase) }
}
}使用依赖倒置进行测试
在测试中,在注入位置用模拟对象或存根替代真实实现。
let mockDB = InMemoryDatabase()
let service = ProfileService(db: mockDB)
// Exercise service
XCTAssertEqual(try mockDB.load(key: "profile"), expectedProfile)快速检查
依赖倒置原则对高层模块有何规定?
课程回顾
依赖倒置原则:高层模块依赖协议抽象,具体类型实现这些抽象。提取精简协议,通过构造器注入依赖,并在组合根中完成所有装配。这样可以为测试、功能开关和未来需求替换实现。
常见问题解答
「使用协议抽象实现依赖倒置」课时是免费的吗?
是的 — 「使用协议抽象实现依赖倒置」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。
「使用协议抽象实现依赖倒置」这节课中我会学到什么?
以 Swift 协议作为边界,依赖抽象而非具体实现。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Swift Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用协议抽象实现依赖倒置」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Swift Academy 课中编写并运行代码吗?
能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 分层:领域、数据与表示层
- 用例与仓储模式
- 使用协议抽象实现依赖倒置
- 不使用 DI 框架组装各层