斯卡拉蛋糕模式鼓励硬编码的依赖关系?
我仍然在尝试学习Scala的蛋糕模式。在我看来,它为您提供了集中配置“组件”的优势,以及为这些组件(当然可覆盖)提供默认实现的能力。斯卡拉蛋糕模式鼓励硬编码的依赖关系?
但是,它使用自我类型特征来描述依赖关系似乎混合了关注点。 Component(我认为)的目的是抽象出该组件的不同实现。但是组件中描述的依赖列表本身是实现问题。
举例来说,假设我有一个数据库全小部件,注册表,让我去查询特定类型的小部件,以及一些排序算法的使用注册表来处理控件:
case class Widget(id: Int, name:String)
trait DatabaseComponent {
def database: (Int => Widget) = new DefaultDatabase()
class DefaultDatabase extends (Int => Widget) {
// silly impl
def apply(x: Int) = new Person(x, "Bob")
}
}
trait RegistryComponent {
this: DatabaseComponent => // registry depends on the database
def registry: (List[Int] => List[Widget]) = new DefaultRegistry()
class DefaultRegistry extends (List[Int] => List[Widget]) {
def apply(xs: List[Int]) = xs.map(database(_))
}
}
trait AlgorithmComponent {
this: RegistryComponent => // algorithm depends on the registry
def algorithm: (() => List[Widget]) = new DefaultAlgorithm()
class DefaultAlgorithm extends (() => List[Widget]) {
// look up employee id's somehow, then feed them
// to the registry for lookup
def apply: List[Widget] = registry(List(1,2,3))
}
}
现在你可以把它一起在一些中央配置:
object Main {
def main(args: Array[String]) {
val algorithm = new AlgorithmComponent() with RegistryComponent with DatabaseComponent
val widgets = println("results: " + algorithm.processor().mkString(", "))
}
}
如果我想换到不同的数据库,我可以注入很容易通过改变我的mixin:
val algorithm = new AlgorithmComponent() with RegistryComponent with SomeOtherDatabaseComponent
但是...如果我想混合使用不同的注册表组件,不使用数据库?
如果我尝试使用不同的(非默认)实现来继承RegistryComponent,RegistryComponent将坚持包含一个DatabaseComponent依赖项。我必须使用RegistryComponent,因为这是最高级的AlgorithmComponent所要求的。
我错过了什么吗?在我的任何组件中使用自我类型的那一刻,我声明所有可能的实现都必须使用这些相同的依赖关系。
有没有其他人遇到过这个问题?什么是解决它的蛋糕式的方式?
谢谢!
随着蛋糕模式,至少example I always go to,你应该将组件的接口定义从它的默认实现中分离出来。这干净地将接口的依赖关系与实现的依赖关系分开。
trait RegistryComponent {
// no dependencies
def registry: (List[Int] => List[Widget])
}
trait DefaultRegistryComponent extends RegistryComponent {
this: DatabaseComponent => // default registry depends on the database
def registry: (List[Int] => List[Widget]) = new DefaultRegistry()
class DefaultRegistry extends (List[Int] => List[Widget]) {
def apply(xs: List[Int]) = xs.map(database(_))
}
}
对。自我类型就像任何其他类型的依赖声明一样。他们可以显示对具体实体(坏)或抽象(好)的依赖。唯一的技巧是cake模式将抽象接口和具体组件都编码为特性,这可能会造成混淆。 – 2012-03-08 18:26:14
就像戴夫说的,你缺少接口,去耦合impls。如果你正在寻找缺少什么蛋糕模式,那么看看EJB和Spring:一个能够意识到事务,安全和资源配置等问题的容器。蛋糕模式并没有解决这个问题,因此,就像Google Guice一样,它只是轻量级的。 – 2012-03-09 07:13:13