使用Reader Monad进行依赖注入

时间:2012-06-30 17:52:54

标签: scala dependency-injection functional-programming monads

我最近看到了关于与Monads的DI的会谈Dead-Simple Dependency InjectionDependency Injection Without the Gymnastics,并给我留下了深刻的印象。我试图将它应用于一个简单的问题,但是一旦它变得非常重要就失败了。我真的希望看到依赖注入的运行版本

  • 依赖于多个必须注入的值的类
  • 依赖于依赖于要注入的东西的类的类

如以下示例

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看一个示例实现真的很有帮助,因为我觉得我错过了正确方向的推动。

谢谢!

1 个答案:

答案 0 :(得分:26)

“读者monad”只是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))