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")
}

通过导入声明?

我考虑的一种可能性是通过控制器中的导入语句。在这里,请求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两个映射而不是整个对象作为隐式参数。

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

4

2 回答 2

3

我非常偏爱为这类事情打包对象。有关说明,请参阅Scala 2.8 中的新增功能:打包对象。包对象有效地允许您将隐式类放入包中,否则您无法做到这一点。

但是,这种方法的主要问题是您不能将对象的定义拆分到多个源文件中,因此由于需要包对象中定义隐式类,因此它们也需要全部位于同一个源文件中. 如果您希望导入许多隐式类,这可能会导致源文件大而笨重。然而,这本身就表明你有一个应该拆分的“包神对象”。

于 2013-10-13T14:10:24.033 回答
3

我知道定义隐式的方法之一是使用包对象。

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")}
}

有点无用的例子,但希望你明白。现在,当longOptgets时l,它被转换为Option[Long]使用隐式。

只要您在包对象定义的同一包中工作,就不需要导入语句。

于 2013-10-13T06:03:16.080 回答