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

带有隐式参数的Scala设计模式(Scala中的Play 2.x),scala,playframework,playframework-2.0,implicit,Scala,Playframework,Playframework 2.0,Implicit,我正在做一个Play2.1项目,需要一些关于scala设计问题的指导。 对于我们的应用程序,在模型层需要一个用于存储来自传入请求的客户机信息的请求上下文对象 case class ClientContext(clientName: String) object ClientContext { def apply(request: Request) = { new ClientContext(request.params("clientName")) //pseudo code

我正在做一个Play2.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)
  }
}

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

有没有办法通过动作包装器和隐式值来改进代码?我希望能够将
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))
  }
} 
用法

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


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

我使用隐式ITS实现了类似的功能:

我叫我的
标题
,而不是
上下文
,但我们都在做同样的事情

我的所有控制器都在
标题中
特征:

object Accounts extends AuthController with Header { ... }
我的
标题
特征如下所示:

trait Header {
    implicit def withUserInfo(implicit maybeUser: Option[User]): UserInfo = {
        // create user info object
    }
}
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 index = MaybeAuthenticated { implicit maybeUser => implicit request =>
    // do stuff
    val foo = new Foo()
    Ok(views.html.accounts.index(foo))
}
其中模板具有如下方法签名:

@(foo: Foo)(implicit userInfo: UserInfo)
MaybeAuthenticated
只是一个可以选择性地恢复
用户
对象的操作,它来自play20身份验证模块。事实上,我已经向您展示了两种可能性:

  • 用隐式函数混合一个特征,该函数采用隐式参数
  • 编写自己的操作方法,如
    MaybeAuthenticated
  • MaybeAuthenticated
    如下所示:

    trait Header {
        implicit def withUserInfo(implicit maybeUser: Option[User]): UserInfo = {
            // create user info object
        }
    }
    
    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 with UserInfo(隐式可能用户:选项[User]):UserInfo 在混合在
    标题中的对象中,此方法将在范围内。当
    选项[User]
    已经在作用域中时,编译器将搜索需要
    UserInfo
    对象在作用域中的函数。编译器将隐式调用
    withUserInfo
    ,以提供缺少的
    UserInfo
    对象

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


    希望这能澄清一点。多亏了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)
    }
    

    我不确定是否有办法摆脱
    隐式请求
    ,但现在这对我来说非常简单

    Ryan,非常感谢你的详细回答。似乎我需要一些时间来理解它。我已经添加了一个关于隐式的简短解释,希望能有所帮助。谢谢Ryan,请原谅我在scalait上的迟钝。最让我困惑的是咖喱动作小说
    隐式请求
    。您必须将请求纳入范围。