如何在不使用硬编码的情况下使用 Cake 依赖注入模式?

我只是阅读和欣赏 蛋糕图案的文章。然而,在我看来,使用依赖注入的一个关键原因是,您可以改变 XML 文件或命令行参数所使用的组件。

如何用 Cake 模式处理 DI 的这个方面?我看到的例子都涉及到在静态中混合特性。

41601 次浏览

Lift 内置了一些类似的东西,大部分是 Scala 代码,但是有一些运行时控制

Scala 也是一种脚本语言。所以你的配置 XML 可以是一种 Scala 脚本。它是类型安全的,而且不是一种不同的语言。

简单看看创业公司:

scala -cp first.jar:second.jar startupScript.scala

与下列情况没有什么不同:

java -cp first.jar:second.jar com.example.MyMainClass context.xml

您总是可以使用 DI,但是您还有一个工具。

由于混合 trait 是在 Scala 中静态完成的,如果你想改变混合到对象中的 trait,根据某些条件创建不同的对象。

让我们以一个规范的蛋糕模式为例。您的模块被定义为 trait,您的应用程序被构造为一个简单的 Object,其中混合了许多功能

val application =
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup

现在,所有这些模块都有很好的自类型声明,它们定义了它们的模块间依赖关系,因此,只有当所有的模块间依赖关系都存在、唯一且类型良好时,行才会进行编译。特别是,持久性模块有一个自我类型,该类型说明任何实现持久性的东西都必须实现 DataSource,这是一个抽象的模块特性。因为 ProductionDataSource 继承自 DataSource,所以一切都很好,并且编译应用程序构造行。

但是,如果您希望使用不同的 DataSource,为了测试的目的而指向某个本地数据库,该怎么办呢?进一步假设您不能使用从某些属性文件加载的不同配置参数重用 ProductionDataSource。在这种情况下,您要做的是定义一个扩展了 DataSource 的新 trait TestDataSource,并将其混合在一起。您甚至可以根据命令行标志动态地这样做。

val application = if (test)
new Object
extends Communications
with Parsing
with Persistence
with Logging
with TestDataSource
else
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource


application.startup

现在看起来比我们希望的要详细一些,特别是如果您的应用程序需要在多个轴上改变其构造。从积极的一面来看,通常您只有一个类似于应用程序中的条件构造逻辑块(或者在每个可识别组件生命周期中最糟糕的一次) ,所以至少痛苦被最小化,并且与您的其他逻辑隔离开来。

简而言之,Scala 目前还没有任何对动态混合的内置支持。

我正在开发自动代理插件来支持这个功能,尽管它目前暂停使用直到2.9版本,到那时编译器将会有新的特性,使它变得更加容易。

同时,实现几乎完全相同功能的最佳方法是将动态添加的行为实现为包装类,然后将隐式转换添加回包装成员。

在 AutoProxy 插件可用之前,达到这种效果的一种方法是使用委托:

trait Module {
def foo: Int
}


trait DelegatedModule extends Module {
var delegate: Module = _
def foo = delegate.foo
}


class Impl extends Module {
def foo = 1
}


// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations

但是要注意,这样做的缺点是更加冗长,如果在 trait 中使用 var,则必须注意初始化顺序。另一个缺点是,如果在上面的 Module中存在与路径相关的类型,那么就不能那么容易地使用委托。

但是如果有大量不同的实现可以改变,那么与列出所有可能组合的案例相比,可能会花费更少的代码。