首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Scala依赖注入:隐式参数的替代方案

Scala依赖注入:隐式参数的替代方案
EN

Stack Overflow用户
提问于 2011-12-08 19:38:09
回答 5查看 7.1K关注 0票数 21

请原谅这条问题的长度。

我经常需要在代码的某一层创建一些上下文信息,然后在其他地方使用这些信息。我通常会使用隐式参数:

代码语言:javascript
复制
def foo(params)(implicit cx: MyContextType) = ...

implicit val context = makeContext()
foo(params)

这是可行的,但需要大量传递隐式参数,这会在布局中间函数后污染层的方法签名,即使它们自己并不关心它。

代码语言:javascript
复制
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模式)。我也不想依赖像Guice或Spring这样的DI/IoC库。我发现它们非常笨重,不太适合Scala。

因此,我认为我需要的是让mog在运行时为它检索正确的上下文对象,有点像一个带有堆栈的ThreadLocal

代码语言:javascript
复制
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中传递上下文信息的方法,它不会污染中间的方法签名,不会静态地将上下文烘焙到接收器中,并且仍然是类型安全的?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-12-09 02:06:46

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可以在Scalaz http://code.google.com/p/scalaz/中找到。然而,ReaderMonad确实“污染”了您的签名,因为它们的类型必须更改,而且通常单调编程会导致代码的大量重构,如果性能或内存是一个问题,则所有闭包的额外对象分配可能不会很好。

这两种技术都不会通过参与者消息发送自动共享上下文。

票数 11
EN

Stack Overflow用户

发布于 2012-05-08 14:26:59

有点晚了,但您是否考虑过在类构造函数中使用隐式参数?

代码语言:javascript
复制
class Foo(implicit biz:Biz) {
   def f() = biz.doStuff
}
class Biz {
   def doStuff = println("do stuff called")
}

如果您希望每次调用f()都有一个新的biz,您可以让隐式参数成为一个返回新biz的函数:

代码语言:javascript
复制
class Foo(implicit biz:() => Biz) {
   def f() = biz().doStuff
}

现在,您只需在构造Foo时提供上下文。你可以这样做:

代码语言:javascript
复制
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()
}

请注意,隐式biz方法在UI中不可见。因此,我们基本上隐藏了这些细节:)

我写了一篇关于using implicit parameters for dependency injection which can be found here的博客文章(无耻的自我推广;)

票数 7
EN

Stack Overflow用户

发布于 2011-12-09 01:23:56

我认为来自lift的依赖注入做了你想要的。有关使用doWith ()方法的详细信息,请参阅wiki

请注意,即使您没有运行lift,也可以将其用作单独的库。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8430446

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档