我刚读了 Cake 模式的文章,并且很喜欢它。但是,在我看来,使用依赖注入的一个关键原因是您可以通过 XML 文件或命令行参数来改变正在使用的组件。
那么,在 Cake 模式中如何处理 DI 的这个方面呢?我看到的所有示例都涉及静态地混合特质。
我刚读了 Cake 模式的文章,并且很喜欢它。但是,在我看来,使用依赖注入的一个关键原因是您可以通过 XML 文件或命令行参数来改变正在使用的组件。
那么,在 Cake 模式中如何处理 DI 的这个方面呢?我看到的所有示例都涉及静态地混合特质。
由于Scala中的混入特质是静态完成的,如果您想要对混入到对象中的特质进行变化,可以基于某些条件创建不同的对象。
让我们以典型的“蛋糕模式”为例。您的模块被定义为特质,而应用程序则构建为一个简单的对象,其中混合了许多功能。
val application =
new Object
extends Communications
with Parsing
with Persistence
with Logging
with ProductionDataSource
application.startup
现在,所有这些模块都有很好的自我类型声明来定义它们之间的模块间依赖关系,所以只有在您的所有模块间依赖项存在、唯一且类型正确时,该行才会被编译。特别地,Persistence模块具有一个自我类型,它表示实现Persistence必须同时实现DataSource,这是一个抽象的模块trait。由于ProductionDataSource继承自DataSource,一切都很棒,应用程序构造行 编译通过。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也是一种脚本语言。因此您的配置XML可以是一个Scala脚本。它是类型安全的,不是另一种语言。
只需查看启动即可:
scala -cp first.jar:second.jar startupScript.scala
并不是非常不同于:
java -cp first.jar:second.jar com.example.MyMainClass context.xml
您始终可以使用 DI,但您还有另一个工具。
在 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
上存在路径相关类型,则无法轻松使用委托。
但是,如果有大量不同的实现可以变化,那么与列出所有可能组合的情况相比,它可能会为您节省代码。
Lift框架内置了类似的功能。它主要是用Scala编写的,但你可以在运行时进行一些控制。http://www.assembla.com/wiki/show/liftweb/Dependency_Injection
val application = ...
- 你有能力使用这些依赖项定义你的应用程序。 然而,假设你的主要应用程序是一个Java类,其中包含Java代码使用的Scala库。在这种情况下,如何使用“生产”类呢? - Kevin Meredith