【发布时间】:2012-01-15 20:34:42
【问题描述】:
请原谅这个问题的长度。
我经常需要在代码的某一层创建一些上下文信息,并在其他地方使用这些信息。我通常发现自己使用隐式参数:
def foo(params)(implicit cx: MyContextType) = ...
implicit val context = makeContext()
foo(params)
这可行,但需要大量传递隐式参数,在干预函数布局后污染层的方法签名,即使他们自己不关心它。
def foo(params)(implicit cx: MyContextType) = ... bar() ...
def bar(params)(implicit cx: MyContextType) = ... qux() ...
def qux(params)(implicit cx: MyContextType) = ... ged() ...
def ged(params)(implicit cx: MyContextType) = ... mog() ...
def mog(params)(implicit cx: MyContextType) = cx.doStuff(params)
implicit val context = makeContext()
foo(params)
我觉得这种方法很难看,但它确实有一个优点:它是类型安全的。我确信mog 将接收到正确类型的上下文对象,否则将无法编译。
如果我可以使用某种形式的“依赖注入”来定位相关上下文,这将减轻混乱。引号表明这与 Scala 中常见的依赖注入模式不同。
起点foo 和终点mog 可能存在于系统的不同级别。例如,foo 可能是用户登录控制器,mog 可能正在执行 SQL 访问。可能有很多用户同时登录,但 SQL 层只有一个实例。每次mog 被不同的用户调用时,都需要不同的上下文。所以上下文不能被烘焙到接收对象中,你也不想以任何方式合并两个层(如蛋糕模式)。我也不想依赖像 Guice 或 Spring 这样的 DI/IoC 库。我发现它们很重,不太适合 Scala。
所以我认为我需要的是让mog 在运行时为其检索正确的上下文对象,有点像ThreadLocal 里面有一个堆栈:
def foo(params) = ...bar()...
def bar(params) = ...qux()...
def qux(params) = ...ged()...
def ged(params) = ...mog()...
def mog(params) = { val cx = retrieveContext(); cx.doStuff(params) }
val context = makeContext()
usingContext(context) { foo(params) }
但只要异步参与者参与到链中的任何位置,这种情况就会下降。不管你使用哪个actor库,如果代码在不同的线程上运行,那么它就会丢失ThreadLocal。
那么...我错过了什么技巧吗?一种在 Scala 中按上下文传递信息的方法,它不会污染干预方法签名,不会将上下文静态地烘焙到接收器中,并且仍然是类型安全的?
【问题讨论】:
标签: scala dependency-injection implicits