2012-03-08 5 views
18

Todavía estoy tratando de aprender el patrón de Cake de Scala. Me parece que le da la ventaja de centralizar su configuración de "Componentes", así como la capacidad de proporcionar implementaciones predeterminadas para esos componentes (que, por supuesto, son reemplazables).Scala Cake Pattern ¿Fomenta las dependencias codificadas?

Sin embargo, es el uso de rasgos de auto-tipo para describir dependencias que parece mezclar áreas de preocupación. El propósito del Componente (creo) es abstraer las diferentes implementaciones para ese componente. Pero la lista de dependencias descrita en el Componente es en sí misma una preocupación de implementación.

Por ejemplo, digamos que tengo una base de datos completa de los widgets, un registro que me permite mirar hacia arriba determinados tipos de widgets, y algún tipo de algoritmo que utiliza el registro para procesar los widgets de:

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

Y ahora se puede poner juntos en algún centro de config:

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

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

Si quiero cambiar a una base de datos diferente, puedo inyectarlo fácilmente cambiando mi mixin:

val algorithm = new AlgorithmComponent() with RegistryComponent with SomeOtherDatabaseComponent 


Pero ... ¿qué ocurre si quiero mezclar en un componente de Registro diferente que no utiliza una base de datos?

Si trato de crear una subclase en el RegistryComponent con una implementación diferente (no predeterminada), RegistryComponent insistirá en que incluya una dependencia DatabaseComponent. Y tengo que usar RegistryComponent, porque eso es lo que requiere el AlgorithmComponent de nivel superior.

¿Echo de menos algo? En el momento en que uso un tipo propio en cualquiera de mis Componentes, declaro que todas las implementaciones posibles deben usar esas mismas dependencias.

¿Alguien más ha tenido este problema? ¿Cuál es la forma de resolverlo como si fuera una torta?

Gracias!

+0

Como Dave dice, le faltan interfaces, para desacoplar las impls. Si está buscando lo que falta en el patrón de pastel, observe EJB y Spring: un contenedor que conoce las preocupaciones, como las transacciones, la seguridad y la configuración de los recursos. El patrón de pastel no se ocupa de eso, y como tal, como Google Guice, es ligero. –

Respuesta

17

Con el patrón de tortas, al menos por example I always go to, debe separar la definición de interfaz del componente de su implementación predeterminada. Esto separa limpiamente las dependencias de la interfaz de las dependencias de la implementación.

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

Derecha. Los self-types son como cualquier otro tipo de declaración de dependencia. Pueden mostrar una dependencia en una entidad concreta (mala) o una abstracta (buena). El único truco es que el patrón de pastel codifica tanto las interfaces abstractas como los componentes concretos como rasgos, lo que puede ser confuso. –