设为首页 加入收藏

TOP

Scalaz(16)- Monad:依赖注入-Dependency Injection By Reader Monad(一)
2017-10-10 12:13:30 】 浏览:7261
Tags:Scalaz Monad 依赖 注入 Dependency Injection Reader

  在上一篇讨论里我们简单的介绍了一下Cake Pattern和Reader Monad是如何实现依赖注入的。主要还是从方法上示范了如何用Cake Pattern和Reader在编程过程中解析依赖和注入依赖。考虑到依赖注入模式在编程中的重要性和普遍性,觉着还需要再讨论的深入一些,使依赖注入模式在FP领域里能从理论走向实际。既然我们正在scalaz的介绍系列里,所以这篇我们就着重示范Reader Monad的依赖注入方法。

  再说明一下依赖注入:我们说过在团队协作开发里能够实现软件模块的各自独立开发,原理首先是实现软件模块之间的松散耦合(decoupling)。对依赖注入的学术定义如下:Dependency Inversion Principle

1、上层模块不应该依赖下层模块,反之亦然。它们之间不应该有直接的代码引用。它们应该都依赖一个抽象的中间层,也就是共享接口(interface)。

2、抽象层不应该依赖实现细节,而细节实现则必须依赖抽象层。大家都按照共享的抽象层进行实现细节编程。

这两条足够解释何为软件模块松散耦合以及具体编码的要求了。这样来说不仅在团队协同开发,即使在个人独立开发环境下依赖注入模式也能发挥良好的作用。一是可以按需要把软件切分成功能模块独立编程。通过依赖注入模式,当下层模块进行了调整后是不会影响上层模块的;上层模块可以随时连接不同的下层功能模块实现不同的功能,比如连接一些测试环境实现上层模块的独立测试。

下面我们还是取用上期的示范例子,由简入深,逐步说明Reader依赖注入的原理、组合、结构设计:

还说那个咖啡机例子:包括了一个开关设备、咖啡感应器;如果感应到壶里有咖啡的话,按下开关咖啡机可以开启加热。

下面是功能抽象描述,它们是按照开发条件和环境需要进行具体细分的。细分程度要确保每项功能都可以独立完成编程。

1  trait OnOffDevice { 2  def on: Unit 3  def off: Unit 4  } 5  trait SensorDevice { 6  def isCoffeePresent: Boolean 7   }

这就是一个抽象层。所有开发人员都必须按照这层的功能描述来编程,所谓编程细节依赖与抽象层要求。

我们现在就可以不用理会以上功能是否已经实现,立即进入上层模块的功能组合了。我们只需要申明依赖项目,先从最简单的开始,假如我现在只需要引用OnOffDevice一项依赖的话,可以在伴生对象(companion object)这样申明操作Reader(primitive reader):

1 object OnOffDevice { 2   def on: Reader[OnOffDevice, String] = Reader(_.on) 3   def off:  Reader[OnOffDevice, String] = Reader(_.off) 4 }

由于只有一个依赖,我们可以直接申明功能Reader,把on,off两个函数变成Primitive Reader。假如我们需要函数运算结果的话,只要注入OnOffDevice实例。由于Reader是个Monad,我们可以用map这样写:

1 object OnOffDevice { 2   def onOffDevice: Reader[OnOffDevice,OnOffDevice] = Reader(identity) 3   def on: Reader[OnOffDevice,String] = onOffDevice map { _.on } 4   def off: Reader[OnOffDevice,String] = onOffDevice map { _.off } 5 }

我们用identity构建了一个基础(primitive)Reader,然后以这个基础Reader用map组合成我们需要的功能函数Reader。现在的on,off函数款式(signature)和前面的定义是一样的。现在我们可以实现这些Reader功能:

1 object OnOffService { 2     def on = for { 3         ison <- OnOffDevice.on 4     } yield ison 5     def off = for { 6         isoff <- OnOffDevice.off 7     } yield isoff 8 }

我们抽象化了OnOffDevice,不需要实现依赖项目就可以直接使用这些Reader功能函数:

1 ef trigger = OnOffService.on      //> trigger: => scalaz.Kleisli[scalaz.Id.Id,OnOffDevice,String]

假如现在实现了OnOffDevice实例:

1 class OnOffDeviceImpl extends OnOffDevice { 2     def on = "MockDevice.On"
3     def off = "MockDevice.Off"
4 }

我们可以在最终运行中注入实现的依赖实例来获取最终结果:

1 object MockOnOffDevice extends OnOffDeviceImpl 2 def trigger = OnOffService.on                     //> trigger: => scalaz.Kleisli[scalaz.Id.Id,Exercises.reader1.OnOffDevice,String 3                                                   //| ]
4 val result = trigger(MockOnOffDevice)             //> result : scalaz.Id.Id[String] = SomeDevice.On
5 result === "SomeDevice.On"                        //> res0: Boolean = true

我们看到,对运算结果注入依赖实例后就能得出具体的运算值。

那如果我们需要两项依赖呢?

1 trait OnOffDevice { 2  def on: String 3  def off: String 4 } 5 trait SensorDevice { 6  def isCoffeePresent: Boolean 7 }

我们可以把两个依赖组成一个更大的功能,一个更高一层的依赖:

1 rait Device { //高层组合依赖
2   def onOffDevice: OnOffDevice     //具体依赖
3   def sensorDevice: SensorDevice   //具体依赖
4 } 5 object Device { 6   val device = Reader[Device,Device](identity) 7   val onOffDevice = device map {_.onOffDevice} 8   val sensorDevice = device map {_.sensorDevice} 9 }

所有Reader的注入依赖现在变成Device类型了。依赖的功能Reader变成这样:

1 object OnOffDevice { 2 import Device.onOffDevice 3     def on: Reader[Device,String] = onOffDevice map { _.on } 4     def off: Reader[Device,String] = onOffDevice map { _.off } 5 } 6 object SensorDevice { 7 import Device.senso
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Scalaz(15)- Monad:依赖注入.. 下一篇Scalaz(17)- Monad:泛函状态..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目