2012-06-30 28 views
22

Recientemente vi las conversaciones Dead-Simple Dependency Injection y Dependency Injection Without the Gymnastics sobre DI con Monads y quedé impresionado. Traté de aplicarlo en un problema simple, pero fracasé tan pronto como se volvió no trivial. Realmente me gustaría ver una versión de funcionamiento de la inyección de dependencias dondeUso de Reader Monad para Dependency Injection

  • una clase que depende de más de un valor que tiene que ser inyectada
  • una clase que depende de una clase que depende de algo a inyectar

como en el siguiente ejemplo

trait FlyBehaviour { def fly() } 
trait QuackBehaviour { def quack() } 
trait Animal { def makeSound() } 

// needs two behaviours injected 
class Duck(val flyBehaviour: FlyBehaviour, val quackBehaviour: QuackBehaviour) extends Animal 
{ 
    def quack() = quackBehaviour.quack() 
    def fly() = flyBehaviour.fly() 
    def makeSound() = quack() 
} 

// needs an Animal injected (e.g. a Duck) 
class Zoo(val animal: Animal) 

// Spring for example would be able to provide a Zoo instance 
// assuming a Zoo in configured to get a Duck injected and 
// a Duck is configured to get impl. of FlyBehaviour and QuackBehaviour injected 
val zoo: Zoo = InjectionFramework.get("Zoo") 
zoo.animal.makeSound() 

sería muy útil para ver una implementación de ejemplo utilizando el lector Mónada ya que acabo de tarifa l que me falta un empuje en la dirección correcta.

Gracias!

Respuesta

26

La "mónada del lector" es solo Function1, por lo que todo lo que tiene que hacer es aceptar un argumento que contenga todas las cosas que necesita. Por ejemplo:

trait Config { 
    def fly: FlyBehaviour 
    def quack: QuackBehaviour 
} 

type Env[A] = Config => A 

Ahora bien, si usted quiere construir un Duck basado en este entorno:

val a: Env[Animal] = c => new Duck(c.fly, c.quack) 

Y entonces la construcción de un Zoo basado en que es fácil:

val z: Env[Zoo] = a andThen (new Zoo(_)) 

Usando Scalaz (o con un poco de trabajo por su cuenta) puede hacer uso de algunas sutilezas de sintaxis para "pedir" la configuración c:

val z: Env[Zoo] = for { 
    c <- ask 
} yield new Zoo(Duck(c.fly, c.quack)) 
+0

Gracias por su ejemplo. Tengo curiosidad, ¿eres (o conoces algún proyecto más grande) usando esto en lugar de Spring o Goolge Guice para DI? ¿Es preferible según tu experiencia? –

+2

Sí, estamos usando esto extensivamente y es mucho más preferible. Si alguna vez vuelvo a ver Spring o Guice, será demasiado pronto. – Apocalisp

+0

@Apocalisp, ¿podría ayudarme a aplicar esta solución a http://stackoverflow.com/questions/12341867/how-to-avoid-dependency-injection-in-scala/12363015#12363015? –

Cuestiones relacionadas