2012-04-20 11 views
5

decir que tengo dos dependencias en mi aplicación, una conexión a algún sistema secundario de pub y una conexión a una base de datos. Puedo hacer algo comopatrón de pastel de escama - ¿puedo tener varias capas de pastel?

trait DB { 
    def lookup(query:String):String 
} 

trait PubSub { 
    def subscribe(key:String, callback:String => Any) 
} 

entonces puedo escribir mi lógica como

trait Functionality { this:DB with PubSub => 
    def doSomething() { 
     val key = lookup("get key") 
     subscribe(key, data => println(data)) 
    } 
} 

y luego mi aplicación puede ser como

object Awesome extends App { 

    object repository extends Functionality with DB with PubSub { 
     def lookup(query:String) = "some key" 
     def subscribe(key:String, callback:String => Any) { 
      scala.concurrent.ops.spawn { while(true) { callback(key) ; Thread.Sleep(1000) } } 
     } 
    } 
    repository.doSomething() 
} 

y todo está bien y el bien en el mundo.

Pero, ¿qué ocurre si quiero conexiones a dos subsistemas de pub que comparten la misma implementación de base de datos en la misma aplicación?

quiero hacer algo como

object Awesome2 extends App { 
    object repository extends DB { 
     def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 
     } 

     object connection2 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 
     } 
    } 
} 

donde los objetos en la segunda capa de pastel (implícitamente?) Sorber en la aplicación DB desde el nivel primario.

Pero el compilador Scala me dice

error: object creation impossible, since method lookup in trait DB of type (query:String) String is not defined 
object connection2 extends Functionality with PubSub with DB { 

si hacer lo siguiente entonces se hace lo que yo quiero

object Awesome3 extends App { 
    object repository extends DB { 
     override def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 

      def lookup(query: String): String = repository.lookup(query) 
     } 

     object connection2 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 

      def lookup(query: String): String = repository.lookup(query) 
     } 
    } 
    repository.connection1.doSomething() 
    repository.connection2.doSomething() 
} 

pero esto es un poco desordenado

puedo añadir este rasgo

trait DB_Base extends DB { 

    private val db:DB = this 

    trait DB_Layer extends DB { 
     def lookup(query:String):String = db.lookup(query) 
    } 
} 

y luego los siguientes trabajos

object Awesome4 extends App { 
    object repository extends DB_Base { 
     override def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB_Layer { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 
     } 

     object connection2 extends Functionality with PubSub with DB_Layer { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 
     } 
    }  
    repository.connection1.doSomething() 
    repository.connection2.doSomething() 
} 

así que ahora tengo dos capas. ¿Cómo consigo tres? Siento que estoy perdiendo la trama.

+2

El pastel es alto en calorías y tiene una carga glucémica alta ... y ¿alguna vez has mirado la lista de ingredientes en un pastel en capas? Prefiera alimentos simples y naturales. Es decir, tenga un pastel de una sola capa como máximo y use un def para apuntar a los recursos que necesite que de otra manera necesitarían ser rellenos. (Esto es un comentario porque en realidad no es una solución a su problema, solo una sugerencia que va a ser difícil de manejar.) Dejar caer 'extiende DB_Base' y agregar' {dbbase => 'y soltar' con DB_Layer' y agregar 'def dbLayer = dbbase' o somesuch produce una alternativa explícita pero compacta. –

+0

gracias rex. estoy confundido por tu comentario. primero, ¿qué hace '{dbbase =>' hacer exactamente? No estoy familiarizado con esa sintaxis. y segundo, ¿no se verá el resultado mucho como 'Awesome3'? – dvmlls

+0

@RexKerr tiene razón, pero no veo cómo puede hacer rodar su propia Torta DI (como parece hacerlo OP) sin capas adicionales. Por ejemplo, supongamos que quieres hornear un pastel DAO. Necesitará gestionar la conexión de la base de datos (producción, etapa, prueba), el tipo de conexión (JNDI, JDBC) y el contrato DAO para implementar DAO. Como podemos ver con lo que presenta el OP, el patrón se vuelve complejo, provocando muchos arañazos en la cabeza ;-) Una vez más, no estoy seguro de cómo se puede hacer Torta DI en Scala sin bajar por el agujero – virtualeyes

Respuesta

4

Un comentario no es lo suficientemente grande como para explicarlo, así que aquí hay una respuesta que básicamente dice: "¡No hagas eso!" y sugiere una alternativa.

El problema clave con el que se está tropezando es que quiere tener varias copias de alguna funcionalidad, pero no tiene forma de referirse a ella por su nombre (solo por tipo). La solución es: darle un nombre.

Tomemos su patrón de torta doble.

trait Foo { def foo(s: String): String } 
trait Bar { def bar(s: String, f: String => Any): Any } 
trait Bippy { this: Foo with Bar => 
    def bip(s: String) = bar(foo(s),println) 
} 

bien, grande, podemos mezclar en Bippy a cualquier cosa que implementa Foo with Bar y vamos a ser capaces de bip. Pero, ¿qué ocurre si Foo y Bar se implementan a diferentes niveles? Si en su lugar

trait Bippy { 
    def myFoo: Foo 
    def myBar: Bar 
    def bip(s: String) = myBar.bar(myFoo.foo(s), println) 
} 

parece inicialmente más incómodo. (Lo es). Pero ahora te permite mezclar y combinar en lugar de forzarte a comer pastel de formas cada vez más incómodas. Por ejemplo:

object Foozle extends Foo { theFoo => 
    def foo(s: String) = s.toUpperCase 
    trait BippyImpl extends Bippy { this: Bar => 
    def myFoo = theFoo 
    def myBar = this 
    } 
    object Woozle1 extends BippyImpl with Bar { 
    def bar(s: String, f: String => Any) = f(s) 
    } 
    object Woozle2 extends BippyImpl with Bar { 
    def bar(s: String, f: String => Any) = f(s.reverse) 
    } 
} 

Ahora puede mezclar y combinar cualquier funcionalidad desde cualquier lugar; el único inconveniente es que tienes que nombrarlo.(Aquí hemos creado un rasgo anidado BippyImpl para dividir las partes comunes para Woozles, pero podríamos hacerlo directamente).

Además, no se mezclan los nombres originales del método; tendrá que escribir proxies o referirse a una variable miembro.

Se echa de menos en algunos de los aspectos agradables del patrón de pastel, pero en mi experiencia, termina siendo mucho más claro que un desastre enorme de capas de pastel. Y ahora puede ver que puede anidar tan profundamente como desee y completar los detalles que desee donde sea que lo necesite.

+0

Tengo problemas para aceptar el hecho de que otra capa de pastel de scala no es lo mismo que un contenedor de niño unitario. pero esa es mi carga gracias por tomarse el tiempo para explicar, rex. lo aprecio. – dvmlls

Cuestiones relacionadas