ADT实现状态模式

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对象时,则不需要考虑这一细节,在语言表达上要简洁得多。

作者

8MilesRD

发布于

2020-01-21

更新于

2020-01-21

许可协议

评论