【问题标题】:Dependency injection with Scala使用 Scala 进行依赖注入
【发布时间】:2014-03-25 00:30:55
【问题描述】:

我正在寻找一种在 Scala 中进行依赖注入的方法,有点像 C# 中的 Spring 或 Unity,但我没有发现任何真正有趣的东西。

  • MacWire:我不明白这样做的好处,因为我们必须在 Wire[CASS] 中提供课程。那么,如果您在调用电汇时给出实现,那有什么意义呢?我可以做新的 CASS 一样。
  • 带有自我类型的蛋糕图案:似乎无法回答我要搜索的内容。

所以我决定进行实施并询问您的想法,因为让我感到惊讶的是,以前没有人做过这样的事情。也许我的实现在现实生活中也有很多问题。

所以这里是一个例子:

trait Messenger {
  def send
}

class SkypeMessenger extends Messenger {
  def send = println("Skype")
}

class ViberMessenger extends Messenger {
  def send = println("Viber")
}

我想在这里在我的应用程序的任何地方注入只在一个地方配置的实现:

object App {
  val messenger = Inject[Messenger]

  def main(args: Array[String]) {
    messenger.send
  }
}

注意我在下面定义的 Inject[Messenger] 以及我想要的配置(产品或开发):

object Inject extends Injector with DevConfig

trait ProdConfig {
  this: Injector =>
  register[Messager](new SkypeMessager)
  register[Messager](new ViberMessager, "viber")
}

trait DevConfig {
  this: Injector =>
  register[Messager](new ViberMessager)
  register[Messager](new ViberMessager, "viber")
}

最后是 Injector,它包含了所有的方法 apply 和 register:

class Injector {
  var map = Map[String, Any]()

  def apply[T: ClassTag] =
    map(classTag[T].toString).asInstanceOf[T]

  def apply[T: ClassTag](id: String) =
    map(classTag[T].toString + id).asInstanceOf[T]

  def register[T: ClassTag](instance: T, id: String = "") = {
    map += (classTag[T].toString + id -> instance)
    instance
  }
}

总结:

  • 我有一个类 Injector,它是接口/特征(最终也是一个 id)和实现实例之间的映射。
  • 我们为每个包含寄存器的配置(开发、产品...)定义了一个特征。它还具有对 Injector 的自我引用。
  • 然后我们使用我们想要的配置创建一个 Injector 实例
  • 用法是调用apply方法给出接口类型(最终也是一个id),它将返回实现的实例。

你怎么看?

【问题讨论】:

  • 我认为,如果您提供一个示例,说明为什么现有方法在应用于您的用例时存在缺陷,这将对我有所帮助。这个问题太抽象了,我现在无法给出反馈。
  • 您的实现将如何解决内部依赖关系并不明显。还可以看看subcut,它(几乎)拥有 scala 中运行时 DI 所需的一切。

标签: scala dependency-injection


【解决方案1】:

您的代码看起来很像 Lift Web 框架中的依赖注入。您可以查阅 Lift 源代码以了解其实现方式或仅使用该框架。您无需运行 Lift 应用程序即可使用其库。这是一个小介绍doc。基本上你应该在 Lift 中查看这段代码:

package net.liftweb.http

/**
 * A base trait for a Factory.  A Factory is both an Injector and
 * a collection of FactorMaker instances.  The FactoryMaker instances auto-register
 * with the Injector.  This provides both concrete Maker/Vender functionality as
 * well as Injector functionality.
 */
trait Factory extends SimpleInjector

您还可以查看这个相关问题:Scala - write unit tests for objects/singletons that extends a trait/class with DB connection 我展示了如何使用 Lift 喷油器。

【讨论】:

【解决方案2】:

谢谢大家,

所以我做了我的回答,但来自 Aleksey 的回答非常好。

我通过这个示例更好地理解了蛋糕模式:
https://github.com/freekh/play-slick/tree/master/samples/play-slick-cake-sample
还可以查看其他没有 DI 的实现并进行比较:
https://github.com/freekh/play-slick/tree/master/samples/

因此,蛋糕图案没有像我展示的电梯样式 DI 那样的集中配置。无论如何,我都会使用 Cake 图案,因为它非常适合 Slick。
我不喜欢 Subcut 的是无处不在的隐含。我知道有一种方法可以避免它们,但它对我来说似乎是一种解决方法。

谢谢

【讨论】:

    【解决方案3】:

    要评论MacWire,您可以使用new 是对的——这就是重点:)。 MacWire 只是为了让您从代码中删除一些样板,而不必再次枚举所有依赖项(这已经在构造函数中完成)。

    主要思想是您在“世界尽头”进行接线,在那里组装您的应用程序(或者您可以将其划分为特征模块,但这是可选的)。否则,您只需使用构造函数来表达依赖关系。没有魔法,没有框架。

    【讨论】:

    • Macwire 的供应商怎么样?
    • @MaatDeamon 最简单的方法是简单地使用函数对象,即首先将依赖项定义为函数:val myServiceFactory = (p1: String, p2: Int) => wire[MyService] 或类似,然后将依赖项声明为函数类型:class AnotherService(myServiceFactory: (String, Int) => MyService)
    • 非常感谢亚当,我会考虑尝试您的所有建议。会告诉你结果如何。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-26
    • 2014-01-19
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多