2012-03-08 23 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