18

我仍在尝试学习 Scala 的蛋糕模式。在我看来,它为您提供了集中“组件”配置的优势,以及为这些组件提供默认实现的能力(这些组件当然是可覆盖的)。

然而,它使用自我类型特征来描述依赖关系似乎混合了关注领域。组件(我认为)的目的是抽象出该组件的不同实现。但是组件中描述的依赖列表本身就是一个实现问题

例如,假设我有一个充满小部件的数据库,一个允许我查找特定类型的小部件的注册表,以及使用注册表来处理小部件的某种算法:

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 所需要的。

我错过了什么吗?在我的任何组件中使用自类型的那一刻,我声明所有可能的实现都必须使用这些相同的依赖项。

有没有其他人遇到过这个问题?解决它的Cake-like方法是什么?

谢谢!

4

1 回答 1

17

对于蛋糕模式,至少在我经常去的示例中,您应该将组件的接口定义与其默认实现分开。这清楚地将接口的依赖项与实现的依赖项分开。

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(_))
  }
}
于 2012-03-08T17:22:19.063 回答