/statemachine-demo

java状态机demo

Primary LanguageJava

状态机

应对复杂“变态”过程的利器。

【注】这里所谓的“变态”指的是改变状态。

适用情况

改变目标实体状态时,除了设置状态值,还需考虑原始状态(或其他已有情况),并作相应处理。

例如地铁在启动时,首先只能在停止状态下才能启动,其次还要检查车门是否完全关闭,人员是否安全等等。

如果不采用状态机,那么就要在实现方法中写一大堆的if-else,switch-case。非常的不OO。

现实情况

最近我们有一个卡券的项目。卡券券码生成后具有四种状态:初始(INIT)、启用(OPEN)、绑定(BOUND)、关闭(CLOSE)。

这四种状态之间的转换有点儿复杂,转换之前首先要考虑源状态,并不是所有状态之间都能转换。例如,对于已经绑定(BOUND,已发给会员)的券码,不能再作任何转换(处于简化的目的,这里没考虑会员过期未使用的情况);已启用(OPEN)的券码可以绑定或关闭,但不能再初始化;已关闭的券码,也不能再作任何操作。

有一种近乎直觉的实现方式是直接为券码提供4个API,init,open,bind,close。但这样作有三个缺点:

  • 一是不得不在每个方法中作很多的if-else,switch-case判断,不够优雅
  • 二是要设置很多方法,每种目标状态都要设置一个,如果实体的状态很多,必然会导致方法“爆炸”
  • 三是会违反RESTful原则。状态的改变需要一个动作,这就不得不在接口的URL上引入动词,例如/couponcode/open/{id}, /couponcode/bind/{id}等等。

状态机方案

理想的方案是:实体知道自己的状态,并能针对目标状态自动做出相应的处理。

这就是状态机:

  1. 为每种源状态设置一个“状态对象”,并为该对象的设置几种“状态转换方法”(对应各种目标状态),分别用来表示要达到每种目标状态,该对象要采取的动作
  2. 使用委托模式(delegate),将对实体的处理委托给上述“状态对象”,即改变实体状态的具体动作由状态对象完成
  3. 使用工厂模式,根据当前实体的状态生成状态对象
  4. 状态对象还有一个额外的方法,根据目标状态调用对应的“状态转换方法”
  5. 对外只使用一个update接口,该接口只要接受一个实体对象即可,表示该实体要改变的状态;其实现方法是获取该实体的当前状态,调用工厂获取状态对象,然后执行状态转换方法即可。

具体实现参考

  1. ICouponCodeState是所有状态对象的接口,定义状态转换方法
  2. AbstractCouponCodeState提供方法的缺省实现,默认会抛出异常,表示不支持这种转变
  3. CouponCodeInit(Open,Bound,Close)State对象继承AbstractCouponCodeState,是具体的状态对象实现类,只Override支持的转换,不支持的转换就由基类的方法抛出异常
  4. CouponCodeStateFactory根据源状态生成具体的状态对象
  5. CouponCodeService的update方法只要调用工厂方法获取状态对象,并执行即可