引言:什么是依赖注入,为什么需要它?
\n想象一下你在建造一所房子。你需要砖块、水泥和工具。与其每次都跑到仓库去取,你只需将所有材料一次性订购到工地。依赖注入(DI) 就是同样的原理,但应用于你的代码中。
\n依赖注入是一种设计模式,对象从外部获取其依赖,而不是自己创建它们。这使得代码更灵活、更易于测试和维护。在本文中,我们将以 Kotlin 为例来讲解DI——这是一种非常适合学习该模式的现代编程语言。
\n\n问题:为什么没有DI的代码会变得脆弱?
\n考虑一个典型的例子。假设我们有一个发送电子邮件通知的 UserService 类。没有DI时,它看起来像这样:
class EmailService {\n fun sendEmail(to: String, message: String) {\n println("发送邮件至 $to: $message")\n }\n}\n\nclass UserService {\n private val emailService = EmailService() // 紧耦合!\n\n fun notifyUser(email: String) {\n emailService.sendEmail(email, "你好,用户!")\n }\n}\n问题出在哪里?UserService 被硬编码绑定到了 EmailService 的具体实现上。如果你想要:
\n- \n
- 将 EmailService 替换为 SmsService; \n
- 添加日志记录; \n
- 使用模拟对象编写单元测试, \n
——你就必须修改 UserService 的代码。这违反了 开闭原则,并使测试变得复杂。
\n\n解决方案:Kotlin中的手动依赖注入
\n实现DI的最简单方法是通过构造函数传递依赖。在Kotlin中,这可以非常简洁地完成:
\n// 1. 创建接口(抽象)\ninterface NotificationService {\n fun send(to: String, message: String)\n}\n\n// 2. 实现具体服务\nclass EmailService : NotificationService {\n override fun send(to: String, message: String) {\n println("邮件至 $to: $message")\n }\n}\n\nclass SmsService : NotificationService {\n override fun send(to: String, message: String) {\n println("短信至 $to: $message")\n }\n}\n\n// 3. 通过构造函数注入依赖\nclass UserService(private val notificationService: NotificationService) {\n fun notifyUser(email: String) {\n notificationService.send(email, "你好!")\n }\n}\n\n// 4. 使用\nfun main() {\n val emailService = EmailService()\n val userService = UserService(emailService) // 注入!\n userService.notifyUser("user@example.com")\n}\n这种方法的优点:
\n- \n
- 灵活性:可以轻松地将
EmailService替换为SmsService; \n - 可测试性:在测试中,你可以传入模拟对象: \n
class MockNotificationService : NotificationService {\n var lastMessage: String? = null\n override fun send(to: String, message: String) {\n lastMessage = message\n }\n}\n\nfun test() {\n val mock = MockNotificationService()\n val userService = UserService(mock)\n userService.notifyUser("test@test.com")\n assert(mock.lastMessage == "你好!")\n}\n\nDI框架:Kotlin中的Koin和Dagger
\n在手动DI中,如果项目很大,你会编写大量样板代码。这时DI框架就派上用场了。在Kotlin世界中,有两个流行的框架:Koin(简单)和 Dagger/Hilt(强大)。
\n\nKoin:简单与快速
\nKoin 是一个用Kotlin编写的轻量级DI框架。它不需要代码生成,