22

Recentemente ho visto i colloqui Dead-Simple Dependency Injection e Dependency Injection Without the Gymnastics su DI con Monads e sono rimasto impressionato. Ho provato ad applicarlo su un problema semplice, ma non sono riuscito non appena è diventato banale. Mi piacerebbe davvero vedere una versione in esecuzione di iniezione di dipendenza in cuiUtilizzo di Reader Monad per Iniezione delle dipendenze

  • una classe che dipende da più di un valore che deve essere iniettato
  • una classe che dipende da una classe che dipende da qualcosa da iniettare

come nel seguente esempio

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

sarebbe veramente utile per vedere un esempio di implementazione utilizzando il lettore di Monade dato che ho appena tassa l che mi manca una spinta nella giusta direzione.

Grazie!

risposta

26

Il "lettore monade" è solo Function1, quindi tutto ciò che devi fare è accettare un argomento contenente tutte le cose che ti servono. Per esempio:

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

type Env[A] = Config => A 

Ora, se si vuole costruire una Duck sulla base di questo ambiente:

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

E poi la costruzione di un Zoo sulla base che è facile:

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

Utilizzando Scalaz (o con un po 'di lavoro da soli) è possibile utilizzare alcune sottigliezze di sintassi per "chiedere" la configurazione c:

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

Grazie per il tuo esempio. Sono curioso, sei (o sei a conoscenza di progetti più grandi) che usa questo al posto di Spring o Goolge Guice per DI? È preferibile secondo la tua esperienza? –

+2

Sì, lo stiamo usando ampiamente ed è molto preferibile. Se mai rivedrò Spring o Guice, sarà troppo presto. – Apocalisp

+0

@Apocalisp, potresti aiutarmi ad applicare questa soluzione a http://stackoverflow.com/questions/12341867/how-to-avoid-dependency-injection-in-scala/12363015#12363015? –