Scala依赖注入:隐式参数的替代
请原谅这个问题的长度。
我经常需要在我的代码的一层创建一些上下文信息,并在其他地方消耗这些信息。我通常发现自己使用隐式参数: 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被不同的用户调用,需要不同的上下文。所以上下文不能被烘烤到接收对象中,也不想以任何方式合并这两个层(像Cake Pattern)。我也宁愿不依赖于DI / IoC库,如Guice或Spring。我发现他们非常沉重,不太适合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标准库包括一些类似你假设的“usingContext”,称为DynamicVariable。这个问题有一些关于它的信息
When we should use scala.util.DynamicVariable?。 DynamicVariable确实使用了ThreadLocal,所以很多与ThreadLocal有关的问题仍然存在。
阅读器monad是明确传递环境的功能性替代方法http://debasishg.blogspot.com/2010/12/case-study-of-cleaner-composition-of.html. Reader monad可以在Scalaz http://code.google.com/p/scalaz/中找到。但是,ReaderMonad会“污染”您的签名,因为它们的类型必须改变,并且一般的monadic编程可能会导致很多重组到你的代码加上额外的对象分配对于所有的关闭可能不会很好,如果性能或内存是一个问题。 这两种技术都不会自动共享一个上下文到一个actor消息发送。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |