具有隐式参数的 Scala 设计模式(在 Scala 中 Play 2.x)

标签 scala playframework playframework-2.0 implicit

我正在开发一个 play 2.1 项目,需要一些有关 scala 设计问题的指导。 对于我们的应用程序,模型层需要一个请求上下文对象来存储来自传入请求的客户端信息。

case class ClientContext(clientName: String) 

object ClientContext {
  def apply(request: Request) = {
    new ClientContext(request.params("clientName")) //pseudo code
  }
}

我的模型

object MyDAO {
  def findAll(context: ClientContext) = { ... }
}

然后在我们的 Controller 中,我们需要将其传递到模型的 dao 方法中:

object MyController extends Controller {
  def index = Action { implicit request => 
    val results = MyDAO.findAll(ClientContext(request))
    Ok(results)
  }
}

隐式请求Action类提供(我想)这种方法的问题是我需要编写隐式请求=>ClientContext(request) 用于调用 MyDAO.findAll 的每个 Controller 操作。

有没有办法通过 Action 包装器和隐式值来改进代码?我希望能够将 context: ClientContext 声明为 MyDAO.findAll 方法中的隐式参数,并按以下方式编写我的操作:

object MyDAO {
  def findAll(implicit context: ClientContext) = { ... }
}

def index = ActionWithContext {
  val results = MyDAO.findAll
  Ok(results)
} 

是否可以编写一个 ActionWithContext(具有 apply 方法的方法或对象)来实现这一目标?我现在最接近的是以下

def ActionWithContext(action: ClientContext => Result) = {
  Action { implicit request =>
    action(ClientContext(request))
  }
} 

使用

def index = ActionWithContext { context => 
  val results = MyDAO.findAll(context)
  Ok(results)
}

任何改进此设计的建议都会有所帮助。谢谢!

PS:老实说,如果这是在 Java 上,我什至不会考虑进一步简化代码,但由于它是 scala,我认为这可能是学习一些 scala 模式的好机会。

最佳答案

我已经使用隐式实现了类似的东西:

我称我的 Header 而不是 Context,但我们都在完成同样的事情。

我的所有 Controller 都混合了 Header 特征:

object Accounts extends AuthController with Header { ... }

我的 Header 特征看起来像这样:

trait Header {
    implicit def withUserInfo(implicit maybeUser: Option[User]): UserInfo = {
        // create user info object
    }
}

然后我可以像这样编写我的 Controller 操作:

def index = MaybeAuthenticated { implicit maybeUser => implicit request =>
    // do stuff
    val foo = new Foo()
    Ok(views.html.accounts.index(foo))
}

模板具有如下所示的方法签名:

@(foo: Foo)(implicit userInfo: UserInfo)

MaybeAuthenticated 只是一个可以选择恢复 User 对象的操作,它来自 play20-auth 模块。事实上,我在这里向您展示了两种可能性:

  1. 将特征与采用隐式参数的隐式函数混合。
  2. 编写您自己的操作方法,例如 MaybeAuthenticated

MaybeAuthenticated 看起来像这样:

private def maybeAuthenticated(f: Option[Account] => Request[AnyContent] => Result): Action[AnyContent] = {
    Action(BodyParsers.parse.anyContent)(req => f(restoreUser(req))(req))
}

protected def MaybeAuthenticated = maybeAuthenticated _

我认为第一种方法更容易理解。

编辑:我认为有必要对隐式进行进一步的解释。

让我们考虑一下上面使用隐式的地方:

隐式 def withUserInfo(隐式 MaybeUser: Option[User]): UserInfo

在混合在 Header 中的对象中,此方法将在范围内。编译器将搜索需要 UserInfo 对象位于 Option[User] 已在范围内的函数。编译器将隐式调用withUserInfo来提供缺少的UserInfo对象。

请注意我的模板所需的隐式 UserInfo 对象。当我调用此模板函数(Ok(...) 调用)时,编译器必须填充隐式 UserInfo 对象。它将通过调用 withUserInfo 并传递范围内隐式 maybeUser 来实现此目的。

希望能够澄清一些隐含的内容。

关于具有隐式参数的 Scala 设计模式(在 Scala 中 Play 2.x),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15578044/

相关文章:

java - ebean 映射到 BYTEA 是什么数据类型?

java - Apache Spark : StackOverflowError when trying to indexing string columns

java - 如何从 Option[Map[String,Seq[String]]] 中知道是否包含键?

java - 如何正确加载本地库进行 sbt 测试?

java - 使用 Play Framework 的博客 Web 应用程序出现逻辑错误

playframework - 在 Play 中进行负载测试!框架

java - Play 框架 2.0.4 Java : impossible to convert ArrayList of POJOs in JsonNode

scala - 注入(inject)构造函数时出错,java.lang.ClassCastException : Play 2. 5.4 - Scala

scala - 为什么Scala编译不允许作用域方法重载?

Scala 类型推断问题