2012-03-08 8 views
18

Nadal próbuję nauczyć się wzoru tortu Scala. Wydaje mi się, że daje on przewagę centralizacji konfiguracji "komponentów", a także możliwość zapewnienia domyślnych implementacji dla tych komponentów (które oczywiście można zastąpić).Scala Cake Pattern zachęcić uzależnień hardcoded?

Jednak wykorzystanie cech własnego typu do opisywania zależności wydaje się mieszać obszar zainteresowania. Celem komponentu (jak sądzę) jest usunięcie różnych implementacji tego komponentu. Ale lista zależności opisana w Komponencie sama jest problemem implementacyjnym.

Na przykład, powiedzmy, że mam bazę danych pełną widgety, rejestr, który pozwala mi zajrzeć do konkretnych rodzajów widżetów i jakiś algorytm, który używa rejestru do przetwarzania widżety:

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)) 
    } 
} 

I teraz można umieścić go razem w jakimś centralnym config:

object Main { 
    def main(args: Array[String]) { 
    val algorithm = new AlgorithmComponent() with RegistryComponent with DatabaseComponent 

    val widgets = println("results: " + algorithm.processor().mkString(", ")) 
    } 
} 

Jeśli chcę, aby przejść do innej bazy danych, mogę go wstrzyknąć łatwo poprzez zmianę mojego mixin:

val algorithm = new AlgorithmComponent() with RegistryComponent with SomeOtherDatabaseComponent 


Ale ... co jeśli chcę się mieszać w innym komponentem Registry że nie korzysta z bazy danych?

Jeśli spróbuję podklasę składnika RegistryComponent z inną (inną niż domyślna) implementacją, komponent RegistryComponent będzie nalegał, aby dołączyć zależność DatabaseComponent. I muszę użyć RegistryComponent, ponieważ właśnie tego wymaga komponent AlgorithmComponent najwyższego poziomu.

Czy brakuje mi czegoś? W momencie, w którym używam self-type w którymkolwiek z moich komponentów, deklaruję, że wszystkie możliwe implementacje muszą korzystać z tych samych zależności.

Czy ktoś inny napotkał ten problem? Jaki jest podobny do tortu sposób rozwiązania tego problemu?

Dzięki!

+0

Podobnie jak Dave mówi, brakuje interfejsów, aby oddzielić imp. Jeśli szukasz, czego brakuje w strukturze ciasta, spójrz na EJB i Spring: pojemnik, który jest świadomy problemów, takich jak transakcje, bezpieczeństwo i konfiguracja zasobów. Wzór tortu tego nie rozwiązuje i jako taki, jak Google Guice, jest tylko lekki. –

Odpowiedz

17

Przy wzorcu ciasta, przynajmniej przez example I always go to, należy oddzielić definicję interfejsu komponentu od domyślnej implementacji. To czysto oddziela zależności interfejsu od zależności implementacji.

+5

Dobrze. Własne typy są jak wszystkie inne rodzaje deklaracji zależności. Mogą wykazywać zależność od konkretnej jednostki (zła) lub abstrakcyjnej (dobra). Jedyna sztuczka polega na tym, że wzór tortu koduje zarówno abstrakcyjne interfejsy, jak i konkretne elementy jako cechy, co może być mylące. –