如果您仍然必须传递参数,那么依赖注入有什么意义呢?
What's the point of dependency injection if you still have to pass in an argument?
我在理解依赖注入的基本概念时遇到了一些困难。 (我正在使用带有 play-slick 模块的 Play 2.5)假设我有一个 class Users
需要数据库连接。
package models
@Singleton
class Users @Inject() (dbConfigProvider: DatabaseConfigProvider) {
private val db = dbConfigProvider.get[JdbcProfile].db
private val users = TableQuery[UserTable]
private val setupAction = DBIO.seq(users.schema.create)
private val setupFuture: Future[Unit] = db.run(setupAction)
def getAll(): Future[Seq[User]] = setupFuture.flatMap(_ =>
db.run(users.result)
)
// More methods like the previous
}
当我有一个视图需要访问这些方法时,我希望依赖注入系统为我填充 dbConfigProvider
依赖,就像这样。
package views
class UserSearch {
def index(implicit ec: ExecutionContext): Future[String] = Future(
(new Users).getAll().map(seq => seq.map(user => user.name).mkString(" "))
)
}
然而,这给了我一个编译错误,我被迫使 dbConfigProvider
成为我的视图的依赖项并显式传递它。在这种情况下,我最终从调用视图的控制器中获得 dbConfigProvider
。
package views
class UserSearch @Inject (dbConfigProvider: DatabaseConfigProvider) {
def index(implicit ec: ExecutionContext): Future[String] = Future(
(new Users(dbConfigProvider)).getAll().map(seq =>
seq.map(user => user.name).mkString(" "))
)
}
我假设我误解了依赖注入的工作原理。
所以我的问题如下:
那么在我的模型中使用 @Inject()
关键字有什么意义 Users
?
我的设计模式有缺陷吗?我希望 Users
和 UserSearch
成为对象,但是我不能对它们使用依赖注入。
万一有人熟悉 Slick,我的 getAll()
方法是否适合使用 slick?这甚至是编写异步代码的正确方法吗?
DI 的优点之一是易于使用其他实现,这在测试中特别有用,您可以在其中传递对参数的模拟。
例如,如果您收到一个将调用外部 API 的 class (MyExternalClass
) 实例,您可以改为发送一个子 class ( MyExternalSubClass extends MyExternalClass
) 将覆盖调用 API 的方法,并且只是 return 预配置的 json
这里还列出了几个优点(和缺点)(网络上还有许多其他有趣的文章):
感谢@MichaelZajac 的评论,我将 UserSearch
更改为这样声明:
class UserSearch @Inject (users: Users)
现在我的控制器设置如下:
class UsersController @Inject()(userSearch: UserSearch) extends Controller {
def index = Action.async {
implicit request => userSearch.index().map(Ok(_))
}
}
这直接回答了我的第一个问题,看到它的实际效果也回答了我的第二个问题。我现在收到 SQL 错误,但至少我的项目可以编译。后来我想出了我的第三个问题——原来没有理由创建 table 方案,因为在我的情况下它是由播放进化文件完成的,因此我删除了 setupFuture.flatMap
部分,即使它可以很好地工作,并且不会做任何愚蠢的事情,比如创建 table 两次,或者你可能需要在 table creation/start-up.
上做任何其他事情
我在理解依赖注入的基本概念时遇到了一些困难。 (我正在使用带有 play-slick 模块的 Play 2.5)假设我有一个 class Users
需要数据库连接。
package models
@Singleton
class Users @Inject() (dbConfigProvider: DatabaseConfigProvider) {
private val db = dbConfigProvider.get[JdbcProfile].db
private val users = TableQuery[UserTable]
private val setupAction = DBIO.seq(users.schema.create)
private val setupFuture: Future[Unit] = db.run(setupAction)
def getAll(): Future[Seq[User]] = setupFuture.flatMap(_ =>
db.run(users.result)
)
// More methods like the previous
}
当我有一个视图需要访问这些方法时,我希望依赖注入系统为我填充 dbConfigProvider
依赖,就像这样。
package views
class UserSearch {
def index(implicit ec: ExecutionContext): Future[String] = Future(
(new Users).getAll().map(seq => seq.map(user => user.name).mkString(" "))
)
}
然而,这给了我一个编译错误,我被迫使 dbConfigProvider
成为我的视图的依赖项并显式传递它。在这种情况下,我最终从调用视图的控制器中获得 dbConfigProvider
。
package views
class UserSearch @Inject (dbConfigProvider: DatabaseConfigProvider) {
def index(implicit ec: ExecutionContext): Future[String] = Future(
(new Users(dbConfigProvider)).getAll().map(seq =>
seq.map(user => user.name).mkString(" "))
)
}
我假设我误解了依赖注入的工作原理。
所以我的问题如下:
那么在我的模型中使用
@Inject()
关键字有什么意义Users
?我的设计模式有缺陷吗?我希望
Users
和UserSearch
成为对象,但是我不能对它们使用依赖注入。万一有人熟悉 Slick,我的
getAll()
方法是否适合使用 slick?这甚至是编写异步代码的正确方法吗?
DI 的优点之一是易于使用其他实现,这在测试中特别有用,您可以在其中传递对参数的模拟。
例如,如果您收到一个将调用外部 API 的 class (MyExternalClass
) 实例,您可以改为发送一个子 class ( MyExternalSubClass extends MyExternalClass
) 将覆盖调用 API 的方法,并且只是 return 预配置的 json
这里还列出了几个优点(和缺点)(网络上还有许多其他有趣的文章):
感谢@MichaelZajac 的评论,我将 UserSearch
更改为这样声明:
class UserSearch @Inject (users: Users)
现在我的控制器设置如下:
class UsersController @Inject()(userSearch: UserSearch) extends Controller {
def index = Action.async {
implicit request => userSearch.index().map(Ok(_))
}
}
这直接回答了我的第一个问题,看到它的实际效果也回答了我的第二个问题。我现在收到 SQL 错误,但至少我的项目可以编译。后来我想出了我的第三个问题——原来没有理由创建 table 方案,因为在我的情况下它是由播放进化文件完成的,因此我删除了 setupFuture.flatMap
部分,即使它可以很好地工作,并且不会做任何愚蠢的事情,比如创建 table 两次,或者你可能需要在 table creation/start-up.