Scala:将隐式参数纳入范围的惯用方法是什么?

时间:2013-10-12 20:45:48

标签: scala playframework-2.2

背景

我正在尝试了解将隐式对象放入Scala应用程序范围内的最佳实践。

我有一个Playframework 2.2.0(Scala 2.10)Web应用程序,它混合了一个特权用于授权。它检查。 Authenticated对象检查范围内是否存在user_id,尝试从缓存,数据库和Web服务调用中检索用户信息,访问令牌和名为MagicNotebook的数据包对象。如果请求有效,则会将各种对象添加到包装的请求中。

  object Authenticated extends ActionBuilder[AuthenticatedRequest] {
    def invokeBlock[A](request: Request[A],
                       block: (AuthenticatedRequest[A] => Future[SimpleResult])) = {
      request.session.get(userName).map { implicit userId =>
        Cache.getAs[DbUser](userKey).map { user =>
          Cache.getAs[String](accessTokenKey).map { accessToken =>
            Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
              block(AuthenticatedRequest(user, accessToken, notebook, request) )
            }.getOrElse(startOver)
          }.getOrElse {
            requestNewAccessToken(user.token).flatMap { response =>
              persistAccessToken(response).map { accessToken =>
                Cache.getAs[MagicNotebook](magicNotebookKey(userId)).map { notebook =>
                  block(AuthenticatedRequest(user, accessToken, notebook, request))
                }.getOrElse(startOver)
              }.getOrElse(startOver)
            }
          }
        }.getOrElse(startOver) // user not found in Cache
      }.getOrElse(startOver) // userName not found in session
    }
  }
}

case class AuthenticatedRequest[A](user: DbUser,
                                   accessToken: String,
                                   magic: MagicNotebook,
                                   request: Request[A])
    extends WrappedRequest[A](request)

问题

将这些隐含变量纳入范围的最佳方法是什么?

通过隐式类?

我尝试使用隐式伴侣类,使用以下代码:

object Helper {
  implicit class Magical(request: AuthenticatedRequest[AnyContent]) {
    def folderMap = request.magic.fMap
    def documentMap = request.magic.dMap
  }
}

但是,我并没有真正得到隐含的好处:

def testing = Authenticated { implicit request =>
  import services.Helper._
  request.magic.home.folders // doesn't compile
  request.magic.home.folders(Magical(request).ffMap) // compiles, but not implicitly
  Ok("testing 123")
}

通过导入声明?

我考虑的一种可能性是通过控制器内的import语句。这里,请求在范围内有一个MagicNotebook对象,我想将其用作隐式变量。

def testing = Authenticated { implicit request =>
  import request.magic._
  request.magic.home.folders // implicit map is a parameter to the `folder` method
  Ok("testing 123")
}

通过伴侣特质?

在这里,我创建了一个伴随特征,该特征混合到Authenticate特征中,其中包含MagicNotebook对象的两个映射到控制器的范围内。

trait Magic {
  implicit def folderMap[A](implicit request: AuthenticatedRequest[A]) =
    request.magic.fMap
  implicit def docMap[A](implicit request: AuthenticatedRequest[A]) = 
    request.magic.dMap
}

我的偏好是伴侣特质解决方案,但我想知道是否有更好的方式我忽略了。我最后重写了使用隐式变量的方法,使用MagicNotebook的两个映射而不是整个对象作为隐式参数。

但同样,我想知道是否有更好的方法。

2 个答案:

答案 0 :(得分:3)

我知道定义implicits的方法之一是使用Package Objects。

package implicitIdiomatic {
    implicit def nullableLongToOption(l:java.lang.Long) = Option(l)
  }
}

package implicitIdiomatic
class ImplicitIdiomaticTest{
  val l:Long = 1

  longOpt(l)

  def longOpt(l:Option[Long]) = l match {case Some(l1) => println(l1); case None => println("No long")}
}

有点无用的例子,但希望你明白这个想法。现在,当longOpt获得l时,它会使用隐式转换为Option[Long]

只要您使用的是包对象定义的相同包,就不需要import语句。

答案 1 :(得分:2)

对于这类事情,我非常偏爱包装对象。有关说明,请参阅What's New in Scala 2.8: Package Objects。包对象有效地允许您将隐式类放入包中,否则您无法执行此操作。

然而,这种方法的主要障碍是你不能跨多个源文件拆分对象的定义,因为需要在包包中定义隐式类,它们是还需要全部在同一个源文件中。如果您希望导入许多隐式类,则会导致源文件变大且难以处理。但是,这本身就表明你有一个“包god object”,应该拆分。