【问题标题】:Scala design pattern with implicit parameter (Play 2.x in Scala)带有隐式参数的 Scala 设计模式(在 Scala 中播放 2.x)
【发布时间】:2013-03-12 18:20:32
【问题描述】:

我正在开发一个 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) = { ... }
}

然后在我们的控制器中,我们需要将它传递给模型的dao方法:

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

implicit requestAction 类提供(我想)这种方法的问题是我需要为调用 MyDAO.findAll 的每个控制器操作编写 implicit request =>ClientContext(request)

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

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

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

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

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 模式的好机会。

【问题讨论】:

    标签: scala playframework playframework-2.0 implicit


    【解决方案1】:

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

    我打电话给我的Header 而不是Context,但我们都在完成同样的事情。

    我所有的控制器都混合Header trait:

    object Accounts extends AuthController with Header { ... }
    

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

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

    然后我可以像这样编写我的控制器操作:

    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 _
    

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

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

    让我们考虑一下implicit 在上面的使用位置:

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

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

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

    希望澄清一点。

    【讨论】:

    • 瑞恩,非常感谢这个非常详细的答案。看来我需要一些时间才能理解它。
    • 我已经添加了对隐式的简短解释,希望对您有所帮助。
    • 谢谢瑞恩,请原谅我对 scalait 的迟钝主要是让我最困惑的咖喱动作小说
    【解决方案2】:

    感谢 Ryan 的建议,这里是另一种解决方案 在 ClientContext 对象中,使 apply 方法也是一个隐式转换,它接受一个隐式参数

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

    然后在控制器中就可以写了

    def index = Action { implicit request => 
      val results = MyDAO.findAll
      Ok(results)
    }
    

    我不确定是否有办法摆脱implicit request,但这对我来说已经很简洁了。

    【讨论】:

    • implicit request 仍然是必需的。您必须将请求纳入范围。
    猜你喜欢
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多