22

Недавно я видел переговоры Dead-Simple Dependency Injection и Dependency Injection Without the Gymnastics о DI с Monads и был впечатлен. Я попытался применить его к простой проблеме, но потерпел неудачу, как только получился нетривиальный. Я действительно хотел бы видеть бегущую версию инъекции зависимостей, гдеИспользование Reader Monad для инъекций зависимостей

  • класс, который зависит от более чем одного значения, который должен быть введен
  • класса, который зависит от класса, который зависит от чего-то, чтобы быть введен

, как в следующем примере

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

было бы очень полезно, чтобы увидеть пример реализации с использованием читателя Monad, так как я просто плату Мне не хватает толчка в правильном направлении.

Спасибо!

ответ

26

«Читательская монада» - это всего лишь Function1, поэтому все, что вам нужно сделать, это принять аргумент, содержащий все необходимое. Например:

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

type Env[A] = Config => A 

Теперь, если вы хотите построить Duck на основе этой среды:

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

А затем построения Zoo на его основе легко:

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

Использование Scalaz (или с небольшим количеством работы самостоятельно) вы можете использовать некоторые синтаксические тонкости для «запроса» для конфигурации c:

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

Спасибо за ваш пример. Мне любопытно, вы (или знаете какие-либо более крупные проекты), используя это вместо весны или goolge guice для DI? Это предпочтительнее по вашему опыту? –

+2

Да, мы используем это широко, и это намного предпочтительнее. Если я когда-нибудь увижу Весну или Гуис снова, это будет слишком рано. – Apocalisp

+0

@Apocalisp, не могли бы вы помочь мне применить это решение на http://stackoverflow.com/questions/12341867/how-to-avoid-dependency-injection-in-scala/12363015#12363015? –

Смежные вопросы