依赖注入 (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
带入范围而无需扩展Logger
。NeedsALogger
不是 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,构造方法就足够了,因此您可以根据自己的喜好进行选择。我个人喜欢自我类型和蛋糕模式,但我看到很多人也避免使用它。
要继续阅读有关蛋糕模式的具体内容,请查看此内容。如果您想了解更多信息,这是一个很好的下一步。