Sto ancora cercando di imparare il motivo a torta di Scala. Mi sembra che ti dia il vantaggio di centralizzare la configurazione di "Componenti" e la possibilità di fornire implementazioni predefinite per quei componenti (che sono ovviamente sovrascrivibili).Scala Cake Pattern Incoraggiare dipendenze hardcoded?
Tuttavia, l'uso di tratti di tipo self-type per descrivere le dipendenze sembra combinare aree di interesse. Lo scopo della Componente (credo) è quello di astrarre le diverse implementazioni per quel componente. Ma l'elenco delle dipendenze descritto nella Componente è di per sé un'implementazione.
Per esempio, diciamo che ho un database completo di widget, un Registro di sistema che mi permette di cercare specifici tipi di widget, e una sorta di algoritmo che utilizza il Registro di sistema per elaborare i widget:
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))
}
}
E ora si può mettere insieme in qualche config centrale:
object Main {
def main(args: Array[String]) {
val algorithm = new AlgorithmComponent() with RegistryComponent with DatabaseComponent
val widgets = println("results: " + algorithm.processor().mkString(", "))
}
}
Se voglio cambiare a un database diverso, mi può iniettare facilmente cambiando il mio mixin:
val algorithm = new AlgorithmComponent() with RegistryComponent with SomeOtherDatabaseComponent
Ma ... cosa succede se voglio mescolare un componente del Registro diverso che non utilizza un database?
Se si tenta di eseguire la sottoclasse di RegistryComponent con un'implementazione diversa (non predefinita), RegistryComponent insisterà sul fatto che includo una dipendenza DatabaseComponent. E devo usare RegistryComponent, perché è ciò che richiede AlgorithmComponent di primo livello.
Mi manca qualcosa? Nel momento in cui utilizzo un self-type in uno qualsiasi dei miei componenti, dichiaro che tutte le implementazioni possibili devono utilizzare le stesse dipendenze.
Qualcun altro si è imbattuto in questo problema? Qual è il modo migliore di risolverlo?
Grazie!
Come dice Dave, mancano interfacce, per disaccoppiare l'impls. Se stai cercando quello che manca al modello di torta, quindi guarda EJB e Spring: un contenitore che è a conoscenza di problemi quali transazioni, sicurezza e configurazione delle risorse. Il modello di torta non risponde a questo, e come tale, come Google Guice, è solo leggero. –