Kotlin中的依赖注入:初学者简易指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

什么是依赖注入,为什么在Kotlin中需要它?



依赖注入(DI)是现代开发中的关键设计原则之一,尤其是在Kotlin中。如果您刚刚开始编程之旅,这个概念可能看起来复杂。实际上很简单:依赖注入是一种从外部将对象的依赖项传递给对象的方式,而不是在对象内部创建它们。



让我们用一个日常例子来说明。想象一下您正在烤一个蛋糕。与其每次都自己磨面粉、收集鸡蛋和搅拌黄油(在内部创建依赖项),不如直接去商店购买现成的原料(从外部获取依赖项)。DI的工作原理相同:您“订购”现成的组件,而不是手动创建它们。



在Kotlin中,DI尤其重要,因为该语言被广泛用于创建Android应用、基于Ktor的服务器解决方案和微服务。没有DI,代码会变得高度耦合,难以测试和维护。使用DI,您将获得:

  • 灵活性 — 轻松将一个实现替换为另一个
  • 可测试性 — 可以插入模拟对象
  • 代码整洁性 — 类不知道其依赖项是如何创建的


没有依赖注入的问题



考虑一个典型情况。我们有一个与数据库交互的UserService类。没有DI时,它看起来像这样:



class UserService {    private val database = Database() // 硬耦合

fun getUser(id: Int): User? { return database.query("SELECT * FROM users WHERE id = $id") }}


这里有什么问题?首先,UserService自己创建了Database实例。如果我们想将真实数据库替换为测试数据库(例如,内存数据库),就必须修改类内部的代码。其次,单元测试变得不可能——我们无法插入一个假数据库。第三,该类违反了单一职责原则(SRP):它不仅处理用户,还管理数据库的创建。



想象一下,如果有几十个这样的依赖项——代码就会变成“意大利面条”,一切都与一切相关。正是为了解决这个问题,依赖注入才存在。



手动实现依赖注入



最简单的依赖注入方式是通过构造函数传递它们。这称为构造函数注入。让我们重写我们的示例:



class UserService(private val database: Database) {    fun getUser(id: Int): User? {        return database.query("SELECT * FROM users WHERE id = $id")    }}

// 使用方式:val database = Database()val userService = UserService(database)


现在UserService不知道Database是如何创建的。它只是接收一个现成的实例。我们可以轻松地替换数据库:



class FakeDatabase : Database() {    override fun query(sql: String): User? {        return User(1, "测试用户")    }}

// 在测试中:val fakeDb = FakeDatabase()val userService = UserService(fakeDb)


这种方法称为手动DI。对于小型项目来说,这完全足够。您只需在一个地方(例如,在main()或一个专门的容器类中)创建所有依赖项,然后将它们传递到需要的地方。



然而,手动DI有一个缺点:当项目规模扩大时,依赖项的数量会变得巨大。您必须手动跟踪对象的创建顺序,并通过多个层级传递它们。这时,DI框架就派上用场了。



使用Koin进行依赖注入



Koin是一个流行的Kotlin DI框架,它不需要代码生成

博客

书籍推荐