0Pricing
Swift Academy · 课时

为什么需要依赖注入

将构造与使用分离,以便进行测试。

为什么需要依赖注入 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

依赖注入为何重要

依赖注入(DI)是指从外部向对象提供其所需的内容,而不是让对象自行创建这些内容。这样可以解耦组件,也更容易进行测试。

问题:紧密耦合

如果某个类型自行构造协作者,它就会被绑定到这些具体类,既无法重新配置,也无法进行隔离测试。

class Logger {
    func log(_ m: String) { print("LOG: \(m)") }
}
class Service {
    let logger = Logger() // hard-coded dependency
    func run() { logger.log("running") }
}
Service().run()

耦合会妨碍测试

由于 Service 总是创建真正的 Logger,测试无法捕获或伪造日志输出。这个依赖对外部是不可见的。

class Logger { func log(_ m: String) { print(m) } }
class Service {
    let logger = Logger()
    func run() { logger.log("hello") }
}
// No way to swap Logger here.
Service().run()

思路:注入依赖

相反,应将依赖传入。由调用方决定提供哪种实现,这样 Service 就不再需要关注具体实现。

class Logger { func log(_ m: String) { print("LOG: \(m)") } }
class Service {
    let logger: Logger
    init(logger: Logger) { self.logger = logger }
    func run() { logger.log("running") }
}
Service(logger: Logger()).run()

解耦的好处

采用注入后,同一个 Service 可以在生产环境或测试中配合不同的日志记录器使用,而无需修改自身代码。

class Logger { var lines: [String] = []
    func log(_ m: String) { lines.append(m) } }
class Service {
    let logger: Logger
    init(logger: Logger) { self.logger = logger }
    func run() { logger.log("event") }
}
let l = Logger()
Service(logger: l).run()
print(l.lines)

单一职责

DI 支持单一职责原则:类型专注于自身的工作,并将其他关注点委托给注入的协作者。

struct Mailer { func send(_ to: String) { print("Mail to \(to)") } }
struct SignupFlow {
    let mailer: Mailer
    func register(_ email: String) { mailer.send(email) }
}
SignupFlow(mailer: Mailer()).register("a@b.com")

灵活性

您可以用新的方式重新组合对象。连接工作发生在应用的边缘,使核心保持灵活。

struct Engine { let power: Int }
struct Car {
    let engine: Engine
    func describe() { print("Power: \(engine.power)") }
}
Car(engine: Engine(power: 200)).describe()
Car(engine: Engine(power: 90)).describe()

显式依赖

列出依赖的初始化器会明确说明该类型确切需要什么。隐藏的、自行创建的依赖会掩盖这一契约。

struct Repo { func load() -> [Int] { [1, 2, 3] } }
struct Report {
    let repo: Repo // visible dependency
    func total() -> Int { repo.load().reduce(0, +) }
}
print(Report(repo: Repo()).total())

组合优于构造

DI 倾向于从外部组合对象。一个小型组合根负责组装对象图,其余代码只需接收所需的内容。

struct DB { func count() -> Int { 42 } }
struct Stats { let db: DB; func show() { print(db.count()) } }
// Composition root:
let app = Stats(db: DB())
app.show()

不仅适用于大型应用

即使是很小的程序也能从中受益:注入依赖可以在不引入额外框架的情况下替换行为,并让意图更加清晰。

struct Clock { func now() -> Int { 12 } }
struct Greeter {
    let clock: Clock
    func greet() { print(clock.now() < 12 ? "Morning" : "Afternoon") }
}
Greeter(clock: Clock()).greet()

无需框架

Swift 中的 DI 无需任何库。普通初始化器和协议就足够了。接下来的课程将展示构造器注入、协议和模拟。

struct Adder { func add(_ a: Int, _ b: Int) -> Int { a + b } }
struct Calc {
    let adder: Adder
    func run() { print(adder.add(2, 3)) }
}
Calc(adder: Adder()).run()

快速检查

依赖注入的核心理念是什么?

回顾

依赖注入从外部提供协作者,而不是在内部构造它们,从而减少耦合、明确依赖并支持测试。在 Swift 中不需要框架。下一步,您将学习最常见的形式:构造器注入。

常见问题解答

「为什么需要依赖注入」课时是免费的吗?

是的 — 「为什么需要依赖注入」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「为什么需要依赖注入」这节课中我会学到什么?

将构造与使用分离,以便进行测试。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「为什么需要依赖注入」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为什么需要依赖注入
  2. 构造器注入
  3. 基于协议的抽象
  4. 在测试中模拟依赖
← 返回 Swift Academy