ADT(代数数据类型)是函数式语音中一种强大的语言特性,这次用它来实现状态模式
状态模式与策略模式存在某些相似性,它们都可以实现某种算法、业务逻辑的切换。以下是状态模式的定义:
状态模式允许一个对象在其内部状态改变时改变的行为,对象看起来似乎修改了它的类。
状态模式具体表现在:
- 状态决定行为,对象的行为由它内部的状态决定
- 对象的状态在运行期被改变时,它的行为也会因此而改变。从表面上看,同一个对象,在不同的运行时刻,行为是不一样的,就像是类被修改了一样
再次与策略模式做对比,策略模式通过在客户端切换不同的策略实现来改变算法;而在状态模式中,对象通过修改内部的状态来切换不同的行为方法。
现在我们举一个饮水机的例子,假如一个饮水机有3种工作状态,分别为未启动、制冷模式、制热模式,那么可以用密封类来封装一个代表不同饮水机状态的ADT。
sealed class WaterMachineState(open val machine: WaterMachine) { fun turnHeating() { if (this !is Heating) { print("turn heating") machine.state = machine.heating } else { print("The state is already heating mode.") } }
fun turnCooling() { if (this !is Cooling) { print("turn cooling") machine.state = machine.cooling } else { print("The state is already cooling mode.") } } fun turnOff() { if (this !is Off) { print("turn off") machine.state = machine.off } else { print("The state is already off mode.") } } }
class Off(override val machine: WaterMachine) : WatermachineState(machine) class Heating(override val machine: WaterMachine) : WatermachineState(machine) class Cooling(override val machine: WaterMachine) : WatermachineState(machine)
|
以上代码分析:
- WatermachineState是一个密封类,拥有一个构造参数为WaterMachine类对象
- 在WatermachineState类外部我们分别定义了Off,Heating,Cooling来代表饮水机的3种不同的工作状态,它们都继承了WaterMachineState类的machine成员属性及3个状态切换的方法
- 在每个切换状态的方法中,我们通过改变machine对象的state,来实现切换饮水机状态的目的
接着看WaterMachine类:
class WaterMachine { var state: WaterMachineState val off = Off(this) val heating = Heating(this) val cooling = Cooling(this) init { this.state = off } fun turnHeating() { this.state.turnHeating() } fun turnCooling() { this.state.turnCooling() } fun turnOff() { this.state.turnOff() } }
|
WaterMachine很简单,内部主要包含了一下成员属性和方法:
- 引用可变的WaterMachineState类对象state,用来表示当前饮水机所处的工作状态
- 分别表示3种不同状态的成员属性,off、heating、cooling,它们也是WaterMahineState类的3种子类对象;它们通过传入this进行构造,从而实现在WaterMachineState状态类内部,改变WaterMachine类的state引用值;当WaterMachine类对象初始化时,state默认为off,即饮水机处于未启动状态
- 3个直接调用的饮水机操作方法,分别执行对应state对象的3种操作,供客户端调用
如果办公室的小伙伴都喜欢喝冷水,早上一来就会把饮水机调整为制冷模式,但Shaw有吃泡面的习惯,他想泡面的时候,就会把饮水机变为制热,所以每次他吃了泡面,下一个喝水的同事就需要再切换回制冷。最后要下班了,Kim就会关闭饮水机的电源。
为了满足这一需求,我们设计了一个waterMachineOps函数:
enum class Moment { EARLY_MORNING, DRINKING_WATER, INSTANCE_NOODLES, AFTER_WORK }
fun waterMachineOps(machine: WaterMachine,moment: Moment) { when(moment) { EARLY_MORNING, DRINKING_WATER -> when(machine.state) { !is WatermachineState.Cooling ->machine.turnCooling() } INSTANCE_NOODLES -> when(machine.state) { !is WatermachineState.Heating -> machine.turnHeating() } AFTER_WORK -> when(machine.state) { !is WatermachineState.Off -> machine.turnOff() } } }
|
这个方法很好地处理了不同角色在不同需求场景下,应该对饮水机执行的不同操作。此外,当用when表达式处理枚举类时,默认的情况必须用else进行处理。然而,由于密封类在类型安全上的额外设计,我们在处理machine对象的state对象时,则不需要考虑这一细节,在语言表达上要简洁得多。