2012-03-08 61 views
18

我仍然在尝试学习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所要求的。

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

有没有其他人遇到过这个问题?什么是解决它的蛋糕式的方式?

谢谢!

+0

就像戴夫说的,你缺少接口,去耦合impls。如果你正在寻找缺少什么蛋糕模式,那么看看EJB和Spring:一个能够意识到事务,安全和资源配置等问题的容器。蛋糕模式并没有解决这个问题,因此,就像Google Guice一样,它只是轻量级的。 – 2012-03-09 07:13:13

回答

17

随着蛋糕模式,至少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(_)) 
    } 
} 
+5

对。自我类型就像任何其他类型的依赖声明一样。他们可以显示对具体实体(坏)或抽象(好)的依赖。唯一的技巧是cake模式将抽象接口和具体组件都编码为特性,这可能会造成混淆。 – 2012-03-08 18:26:14