我想知道在Scala中使用函数和DI模式的区别。我想出了以下理解,我想知道这种理解是否正确。
让我们设想一个依赖图。
1)如果我们使用函数作为构建块,那么图形由作为节点的函数和作为边的参数组成。
2)如果我们使用traits作为构建块(如Cake),那么图形由作为节点的特征和作为边缘的抽象成员组成。
那么Cake模式的目的是什么?为什么2优于1?这是课程粒度。通过将函数分组为特征可以简化图形1,然后我们可以使用更小,更易理解的图形-2。相关概念的分组/聚类是一种压缩形式,可以创造理解(我们需要掌握更少的东西才能获得理解)。
这是一个不同的比较(Cake与包系统之间):
Cake类似于将相关函数分组到包中,但它超出了这个范围,因为使用名称空间(包/对象)会导致依赖关系变得难以连接,Cake正在用特征和import
替换包/对象自我类型注释/抽象成员。包和Cake模式之间的区别在于,依赖项的实际实现可以使用Cake进行更改,而使用包时却无法更改。
我不知道这些类比是否有意义,如果不是,请纠正我,如果是,请向我保证。我仍然试图围绕Cake模式,以及如何将它与我已经理解的概念(函数,包)联系起来。
答案 0 :(得分:2)
依赖注入(DI)通常使用getter / setter(我假设你的意思是函数)和/或构造函数params来完成。 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
}
现在,蛋糕图案如何适应?首先,让我们将我们的例子改编为Cake Pattern:
class NeedsALogger {
logger: Logger =>
// Uses a Logger here
}
我们来谈谈logger: Logger =>
。这是一个自我类型,它只是将Logger
的成员带入范围而不必扩展Logger
。 NeedsALogger
不是Logger
,因此我们不想扩展它。但是,NeedsALogger
需要一个Logger
,这就是我们使用自我类型所做的事情。我们要求在创建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,构造函数方法就足够了,所以你可以根据自己的偏好选择。我个人喜欢自我类型和蛋糕模式,但我看到很多人也避免使用它。
要继续阅读有关蛋糕模式的信息,请检查this。如果您想了解更多信息,这是一个很好的下一步。