使用 case class/companion 对象模式时的 Scala 依赖注入
Scala dependency injection when using case class/companion object pattern
在迁移到 Play 2.5 时,我采用了依赖注入设计模式,包括 (JDBC) 数据库访问。
在class水平,我理解这个概念:
class Users @Inject() (db: Database)
但我还没有看到当您需要在案例 class 和伴随对象模式的方法中访问数据库时如何应用它的讨论。一个示例基本模型是:
package models
import anorm._
import anorm.SqlParser._
import javax.inject._
import play.api.db._
import play.api.libs.functional.syntax._
import play.api.libs.json._
case class User @Inject() (db: Database) (
id: Option[Long] = None,
email: String
) {
def save = {
id.map { id => User.findById(id) } match {
case None => create
case _ => update
}
}
def create = db.withConnection { implicit conn =>
SQL(
"""INSERT INTO users (email) VALUES ({email})"""
).on(
'email -> email
).executeUpdate()
this
}
def update = ...
}
object User {
val simple = {
get[Option[Long]]("id") ~
get[String]("email") map {
case id ~ email =>
User(id, email)
}
}
def findById(id: Long) = db.withConnection { implicit conn =>
SQL("""SELECT * FROM users WHERE id = {id}""").on('id -> id).as(User.simple.singleOpt)
}
}
这会更改案例 class 的签名(使其在 val simple = { ... }
中不可用),我不知道如何 inject/access 伴随对象中的数据库。在对象内尝试 @Inject() var db: Database _
会导致我想避免的 NullPointerExceptions 世界。
在依赖注入的世界中,针对此常见用例的推荐设计模式是什么?
一种常见的模式是将数据库功能放在单独的 class 中。在您的情况下,只将数据留给用户:
case class User(id: Option[Long] = None, email: String)
并将数据库功能放入单独的class:
class UserRepository @Inject()(db: Database) {
def save(user: User) = { ... }
def create() : User = { ... }
def findById(id: Long) : Option[User] = { ... }
}
不知道您将如何在代码中使用 User
对象。但是使用该模式,您不会携带对数据库的引用,每个用户对象基本上都会将持久性实现泄漏到使用用户对象的任何地方。也许您想拥有这个,但是在 val simple = ...
中创建 User
对象的方式向我表明您想要创建仅包含数据的用户对象。
现在您正在传递用户对象,只有在需要数据库功能时才注入 UserRepository
。
这并不能完全回答您关于将依赖项注入伴随对象的问题,但无论如何可能会有帮助。
在迁移到 Play 2.5 时,我采用了依赖注入设计模式,包括 (JDBC) 数据库访问。
在class水平,我理解这个概念:
class Users @Inject() (db: Database)
但我还没有看到当您需要在案例 class 和伴随对象模式的方法中访问数据库时如何应用它的讨论。一个示例基本模型是:
package models
import anorm._
import anorm.SqlParser._
import javax.inject._
import play.api.db._
import play.api.libs.functional.syntax._
import play.api.libs.json._
case class User @Inject() (db: Database) (
id: Option[Long] = None,
email: String
) {
def save = {
id.map { id => User.findById(id) } match {
case None => create
case _ => update
}
}
def create = db.withConnection { implicit conn =>
SQL(
"""INSERT INTO users (email) VALUES ({email})"""
).on(
'email -> email
).executeUpdate()
this
}
def update = ...
}
object User {
val simple = {
get[Option[Long]]("id") ~
get[String]("email") map {
case id ~ email =>
User(id, email)
}
}
def findById(id: Long) = db.withConnection { implicit conn =>
SQL("""SELECT * FROM users WHERE id = {id}""").on('id -> id).as(User.simple.singleOpt)
}
}
这会更改案例 class 的签名(使其在 val simple = { ... }
中不可用),我不知道如何 inject/access 伴随对象中的数据库。在对象内尝试 @Inject() var db: Database _
会导致我想避免的 NullPointerExceptions 世界。
在依赖注入的世界中,针对此常见用例的推荐设计模式是什么?
一种常见的模式是将数据库功能放在单独的 class 中。在您的情况下,只将数据留给用户:
case class User(id: Option[Long] = None, email: String)
并将数据库功能放入单独的class:
class UserRepository @Inject()(db: Database) {
def save(user: User) = { ... }
def create() : User = { ... }
def findById(id: Long) : Option[User] = { ... }
}
不知道您将如何在代码中使用 User
对象。但是使用该模式,您不会携带对数据库的引用,每个用户对象基本上都会将持久性实现泄漏到使用用户对象的任何地方。也许您想拥有这个,但是在 val simple = ...
中创建 User
对象的方式向我表明您想要创建仅包含数据的用户对象。
现在您正在传递用户对象,只有在需要数据库功能时才注入 UserRepository
。
这并不能完全回答您关于将依赖项注入伴随对象的问题,但无论如何可能会有帮助。