4

我想知道在 Scala 中为 DI 使用函数和 Cake 模式之间的区别。我想出了以下理解,我想知道这种理解是否正确。

让我们想象一个依赖图。

1)如果我们使用函数作为构建块,那么图由作为节点的函数和作为边的参数组成。

2)如果我们使用特征作为构建块(如在 Cake 中),那么图由作为节点的特征和作为边的抽象成员组成。

那么蛋糕模式的目的是什么?为什么 2 比 1 好?这是当然的粒化。Graph-1 可以通过将函数分组为特征来简化,然后我们有一个更小、更易于理解的 Graph-2。相关概念的分组/聚类是一种压缩形式,可以产生理解(我们需要在头脑中保留更少的东西才能理解)。

这是一个不同的比较(Cake 与包系统之间):

Cake 类似于将相关函数分组到包中,但它超越了这一点,因为使用名称空间(包/对象)会导致依赖关系被硬连接,Cake 正在用特征替换包/对象,import用自类型注释/抽象成员替换 s。包和 Cake 模式之间的区别在于,依赖项的实际实现可以使用 Cake 改变,而使用包时它不能改变。

我不知道这些类比是否有意义,如果没有,请纠正我,如果是,请让我放心。我仍在尝试围绕 Cake 模式以及如何将其与我已经理解的概念(函数、包)联系起来。

4

1 回答 1

2

依赖注入 (DI) 通常使用 getter/setter(我假设您所说的函数)和/或构造函数参数来完成。getter/setter 方法可能看起来像这样:

trait Logger { 
  // fancy logging stuff 
}

class NeedsALogger {
  private var l: Logger = _
  def logger: Logger = l
  def logger_=(newLogger: Logger) {
    l = newLogger
  }
  // uses a Logger here
}

我不太喜欢 getter/setter 方法。不能保证永远注入依赖项。如果您使用某些 DI 框架,您可以强制注入某些东西,但是您的 DI 不再与您的框架无关。现在,如果您使用构造函数方法,则无论何时实例化都必须提供依赖项(无论框架如何):

class NeedsALogger(logger: Logger) {
  // uses a Logger here
}

现在,蛋糕模式如何适应?首先,让我们将我们的示例修改为蛋糕模式:

class NeedsALogger {
  logger: Logger => 
  // Uses a Logger here
}

我们来谈谈logger: Logger =>。这是一个自我类型,它只是将成员Logger带入范围而无需扩展LoggerNeedsALogger不是 a Logger,所以我们不想扩展它。但是,NeedsALogger 需要一个Logger,这就是我们用 self 类型完成的。我们要求Logger在创建NeedsALogger. 用法如下所示:

trait FooLogger extends Logger {
  // full implementation of Logger
}

trait BarLogger extends Logger {
  // full implementation of Logger
}

val a = new NeedsALogger with FooLogger
val b = new NeedsALogger with BarLogger
val c = new NeedsALogger // compile-time error! 

正如你所看到的,我们用任何一种方法都完成了同样的事情。对于很多 DI,构造方法就足够了,因此您可以根据自己的喜好进行选择。我个人喜欢自我类型和蛋糕模式,但我看到很多人也避免使用它。

要继续阅读有关蛋糕模式的具体内容,请查看内容。如果您想了解更多信息,这是一个很好的下一步。

于 2017-11-06T21:25:09.627 回答