首页 > 代码库 > 敏捷软件开发 – STATE模式
敏捷软件开发 – STATE模式
地铁旋转门
最直接的实现FSM策略的方式是使用嵌套switch/case语句。
public enum State { LOCKED, UNLOCKED };
public enum Event { COIN, PASS };
public class TurnStile{ private State state = State.LOCKED; private TurnstileController turnstileController; public TurnStile(TurnstileController action) { this.turnstileController = action; } public void HandleEvent(Event e) { switch (state) { case State.LOCKED: switch (e) { case Event.COIN: turnstileController.Unlock(); break; case Event.PASS: turnstileController.Alarm(); break; } break; case State.UNLOCKED: switch (e) { case Event.COIN: turnstileController.Thankyou(); break; case Event.PASS: turnstileController.Lock(); this.state = State.LOCKED; break; } break; } }}
对于简单的状态机来说,嵌套switch/case实现既简单又优雅。所有的状态和事件都出现在一、两页代码中。然而,对于大型的FSM来说,情况就不同了。在一个具有大量状态和事件的状态机中,代码就退化成一页页的case语句。并且没有方便的定位工具帮助你了解正在阅读的是状态机的哪一部分。维护冗长、嵌套的switch/case语句十一项非常困难并且容易出错的工作。
嵌套switch/case语句实现的另一个代价是有限状态机的逻辑和实现动作的代码之间没有很豪的分离。
迁移表
一个很常见的实现FSM的技术就是创建一个描述迁移的数据表。该表被一个处理事件的引擎解释。引擎查找与事件匹配的迁移,调用相应的动作,并更改状态。
public enum State { LOCKED, UNLOCKED };public enum Event { COIN, PASS };public class TurnStile{ private State state = State.LOCKED; private ILIst transitions = new ArrayList(); private delegate void Action(); public TurnStile(TurnstileController controller) { Action unlock = new Action(controller.Unlock); Action alarm = new Action(controller.Alarm); Action thankyou = new Action(controller.Thankyou); Action lokcAction = new Action(controller.Lock); AddTransition(State.LOCKED, Event.COIN, State.UNLOCKED, unlock); AddTransition(State.LOCKED, Event.PASS, State.LOCKED, alarm); AddTransition(State.UNLOCKED, Event.COIN, State.UNLOCKED, thankyou); AddTransition(State.UNLOCKED, Event.PASS, State.LOCKED, lockAction); } public void HandlerEvent(Event e) { foreach(Transition transition in transitions) { if(state == transition.startState && e == transition.trigger) { state = transition.endState; transition.action; } } } public void AddTransition(State start, Event e, State end, Action action) { transitions.Add(new Transition(start, e, end, action); } private class Transition { public State startState; public Event trigger; public State endState; public Action action; public Transition(State start, Event e, State end, Action action) { this.startState = start; this.trigger = e; this.endState = end; this.action = action; } }}
这种实现方法有一个很大的好处,那就是构建迁移表的代码读起来就像一个规范的状态迁移表。和维护switch/case实现相比,维护这样的一个有限状态机是非常容易的。要增加新的迁移,只需要向Turnstile的构造函数中增加一行AddTransaction语句即可。
该方法的另一个好处是迁移表可以容易地在运行时改变。这样就允许动态地改变状态机逻辑。
该方法的代价主要是速度。对迁移表的遍历需要花费时间。对于大型的状态机来说,所花费的时间就会变得相当客观。
STATE模式
Turnstile类拥有关于事件的Public方法以及关于动作的protected方法。它持有一个指向TurnstileState接口的引用。TurnstileState的两个派生类代表FSM的两个状态。
当Turnstile的两个事件方法中的一个被调用时,它就把这个时间委托给TurnstileState对象。TurnstileLockedState的方法实现了Locked状态下的想要动作。TurnstileUnlockedState的方法实现了Unlocked状态下的相应动作。为了改变FSM的状态,就要把这两个派生类之一的实例赋给Turnstile对象中的引用。
STATE模式彻底地分离了状态机的逻辑和动作。动作是在Context类中实现的,而逻辑则分布在State类的派生类中。这使得二者可以非常容易地独立变化。
另一个好处就是它非常搞笑。它基本上和嵌套switch/case实现的效率完全一样。因此,该方法既具有表驱动方法的灵活性,又具有嵌套switch/case方法的效率。
该项技术的代价体现在两个方面。第一,State派生类的编写完全是一项发威的工作。编写一个具有20个状态的状态机会使人精神麻木。第二,逻辑分散,无法在一个地方就看到整个状态机的逻辑。因此,就使得代码难以维护。
摘录自:[美]RobertC.Martin、MicahMartin著,邓辉、孙鸣译 敏捷软件开发原则、模式与实践(C#版修订版) [M]、人民邮电出版社,2013、433-451、
敏捷软件开发 – STATE模式