rDevice 8 def isCoffeePresent: Reader[Device,Boolean] = sensorDevice map { _.isCoffeePresent } 9 }
我们看到原来的Reader实现细节编码是不需要改变的,如OnOffService。
假设我们获得了下面的功能实现程序:
1 class OnOffDeviceImpl extends OnOffDevice { 2 def on = "SomeDevice.On"
3 def off = "SomeDevice.Off"
4 } 5 class SensorDeviceImpl extends SensorDevice { 6 def isCoffeePresent = true
7 }
由于这次的依赖注入是Device类型的,所以我们需要获取Device实例用来注入到结果Reader:
1 object MockOnOffDevice extends OnOffDeviceImpl 2 object MockSensorDevice extends SensorDeviceImpl 3 trait DeviceImpl extends Device { 4 def onOffDevice = new OnOffDevice { 5 def on = MockOnOffDevice.on 6 def off = MockOnOffDevice.off 7 } 8 def sensorDevice = new SensorDevice { 9 def isCoffeePresent = MockSensorDevice.isCoffeePresent 10 } 11 }
现在这个DeviceImpl是一个Device实例,我们可以把它注入到Reader得出运算结果,还是先运行上面的例子:
1 object MockDevice extends Device with DeviceImpl 2 def trigger = OnOffService.on //> trigger: => scalaz.Kleisli[scalaz.Id.Id,Exercises.reader2.Device,String]
3 val result = trigger(MockDevice) //> result : scalaz.Id.Id[String] = SomeDevice.On
这次是用Device实例注入得出结果的。
我们还可以把依赖项目独立分别定义,这样可以更灵活的组合Device:
1 trait OnOffComponent { 2 def onOffDevice: OnOffDevice 3 } 4 trait SensorComponent { 5 def sensorDevice: SensorDevice 6 } 7 trait Device extends OnOffComponent with SensorComponent
这样我们可以把两个依赖分开来格式化后组合成Device实例,因为我们的注入依赖现在是Device类型的了:
1 object MockOnOffDevice extends OnOffDeviceImpl 2 object MockSensorDevice extends SensorDeviceImpl 3 trait OnOffFunctions extends OnOffComponent { 4 def onOffDevice = MockOnOffDevice 5 } 6 trait SensorFunctions extends SensorComponent { 7 def sensorDevice = MockSensorDevice 8 }
完成了Device实例的组合后可以通过注入Device实例来得取运算结果:
1 object MockDevice extends Device with OnOffFunctions with SensorFunctions 2 def trigger =
3 if (SensorService.isCoffeePresent(MockDevice)) 4 OnOffService.on(MockDevice) 5 else
6 OnOffService.off(MockDevice) //> trigger: => scalaz.Id.Id[String]
7 trigger //> res0: scalaz.Id.Id[String] = SomeDevice.On
如果换另一个版本的SensorDevice实现:
1 class SensorDeviceImpl extends SensorDevice { 2 def isCoffeePresent = false
3 }
重新运行:
1 object MockDevice extends Device with OnOffFunctions with SensorFunctions 2 def trigger =
3 if (SensorService.isCoffeePresent(MockDevice)) 4 OnOffService.on(MockDevice) 5 else
6 OnOffService.off(MockDevice) //> trigger: => scalaz.Id.Id[String]
7 trigger //> res0: scalaz.Id.Id[String] = SomeDevice.Off
正确反应了新的运算结果。
从以上的示范中我们看到了依赖的层次结构以及Reader搭配。我们可以用多层结构来精简基础Reader。但多层式的依赖结构统一了注入依赖类型,最后注入时就无法拆分依赖类型,又会弱化依赖的组合灵活性。所以在组织依赖时应该注意确定在自己的程序中将会使用到所有依赖。这样调用统一的一种注入类型就足够了。
下面再增添多一个依赖:增加一个电源制式检测功能,只有US制式的电源才能启动咖啡机。现在全部功能的抽象描述如下:
1 trait OnOffDevice { 2 def on: String 3 def off: String 4 } 5 trait SensorDevice { 6 def isCoffeePresent: Boolean 7 } 8 trait PowerConfig { 9 def getPowerVolts(country: String): Int 10 def isUSStandard(volt: Int): Boolean 11 }
我们再试试增加多一层结构:
1 trait Device extends OnOffComponent with SensorComponent 2 trait DeviceComponent { 3 def onOffDevice: OnOffDevice 4 def sensorDevice: SensorDevice 5 } 6 trait PowerComponent { 7 def powerConfig: PowerConfig 8 } 9 trait Appliance extends DeviceComponent with PowerComponent 10 object Appliance { 11 val appliance = Reader[Appliance,Appliance](identity) 12 val onOffDevice = appliance map {_.onOffDevice} 13 val sensorDevice