Mar*_*ing 19 dependency-injection scala implicits
请原谅这个问题的长度.
我经常需要在我的代码的一层创建一些上下文信息,并在其他地方使用该信息.我通常发现自己使用隐式参数:
def foo(params)(implicit cx: MyContextType) = ...
implicit val context = makeContext()
foo(params)
Run Code Online (Sandbox Code Playgroud)
这是有效的,但是需要隐式参数传递很多,在介入函数的布局之后污染图层的方法签名,即使他们自己并不关心它.
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)
Run Code Online (Sandbox Code Playgroud)
我发现这种方法很难看,但它确实有一个优点:它的类型安全.我肯定知道mog
会收到正确类型的上下文对象,或者它不会编译.
如果我可以使用某种形式的"依赖注入"来定位相关的上下文,它将减轻混乱.引号表明这与Scala中常见的依赖注入模式不同.
起点foo
和终点mog
可以存在于系统的非常不同的级别.例如,foo
可能是用户登录控制器,mog
可能正在进行SQL访问.可能有许多用户同时登录,但只有一个SQL层实例.每次mog
由不同的用户调用时,需要不同的上下文.因此,上下文不能被烘焙到接收对象中,也不想以任何方式合并这两个层(如Cake Pattern).我还宁愿不依赖像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) }
Run Code Online (Sandbox Code Playgroud)
但是,只要异步行为者涉及链中的任何地方,这种情况就会下降.使用哪个actor库并不重要,如果代码在不同的线程上运行,那么它就会丢失ThreadLocal
.
所以......有一个我不知道的伎俩吗?在Scala中上传信息而不污染介入方法签名的方法,不会静态地将上下文烘焙到接收器中,并且仍然是类型安全的吗?
Jam*_*Iry 11
Scala标准库包含类似于假设的"usingContext",名为DynamicVariable.这个问题有一些关于它的信息当我们应该使用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编程可能会导致对代码进行大量重组,如果性能或内存成为问题,则所有闭包的额外对象分配可能不会很好.
这些技术都不会自动共享演员消息发送的上下文.
派对有点晚了,但您是否考虑过对类构造函数使用隐式参数?
class Foo(implicit biz:Biz) {
def f() = biz.doStuff
}
class Biz {
def doStuff = println("do stuff called")
}
Run Code Online (Sandbox Code Playgroud)
如果你想为每次调用创建一个新的商业,f()
可以让隐含参数成为一个返回新商业的函数:
class Foo(implicit biz:() => Biz) {
def f() = biz().doStuff
}
Run Code Online (Sandbox Code Playgroud)
现在,您只需在构造时提供上下文Foo
.您可以这样做:
trait Context {
private implicit def biz = () => new Biz
implicit def foo = new Foo // The implicit parameter biz will be resolved to the biz method above
}
class UI extends Context {
def render = foo.f()
}
Run Code Online (Sandbox Code Playgroud)
请注意,隐式biz
方法将不可见UI
.所以我们基本上隐藏了那些细节:)
我写了一篇关于使用隐式参数进行依赖注入的博客文章,可以在这里找到(无耻的自我推销;))
归档时间: |
|
查看次数: |
5885 次 |
最近记录: |