2004年5月19日,部门在大厦2号楼9楼大会议室举行有关OO设计思想以及优秀开发工具的技术交流活动。会上,同事<隐去其名字>主讲了两个主题的内容,分别是“State模式”以及“Dunit单元测试工具”。在前一个主题的讲座和技术交流过程中,大家进行了比较充分的讨论。我本人也有一些感想和心得,特此记录,以飨读者。
2 场景
以十字转门为例。
以上是十字转门的状态迁移图(采用UML)。该图的格式具有如下特点:
1. 采用圆角矩形表达可能的所有状态。注意,状态应该采用形容词表达;
2. 使用单箭头的线段表达迁移,迁移由原状态指向目的状态。迁移线段上的文字包含事件和动作两个部分,并使用”/”分割。
综合上图可知:
n 十字转门共有两个状态,分别为Locked以及Unlocked;
n 十字转门共有两种事件,分别为Pass和Coin。
n 通过各个状态对事件的处理,可以得到四种动作,分别为SetLocked,SetUnLocked,Alarm,Thank you。其中,Alarm以及Thank you可看作是对于异常情况的处理。
3 初步实现
简单考虑之后,采用Case来实现以上功能,如:
Tstate = (stLocked, stUnLocked);
TeventType = (etPass, etCoin);
…
case Fstate of
stLocked:
case AeventType of
etPass:
DoAlarm;
etCoin:
DoSetUnLocked;
stUnLocked:
case AeventType of
etPass:
DoSetLocked;
etCoin:
DoThankYou;
end;
可以看到,以上代码通过Case语句的层次嵌套完成了功能。功能尽管已经实现,但我们心里似乎有些不够痛快——是否太繁琐了些?试想如果状态更多,事件也更多,那相应的case语句岂非要写的更长?而且不同状态对不同事件的处理代码集中在一起,造成了不必要的相关性,同时也给代码维护造成了更多笔误的机会(不可否认,逻辑集中也有其好处:阅读代码时方便)。
4 模式实现
上述问题改用State模式来实现,那就是另一种风格和境界了:
仔细研究上述图,不难发现:
1. 门类(TTurnstile)指向了一个代表其状态的一个具体状态类;
2. 把状态进行抽象,并将所有的事件作为抽象(纯虚)方法,然后为每个状态定义派生类来处理各个事件;
3. 相识关系:门类知道状态抽象类,以及所有的具体状态类;具体状态类没有成员变量,也不知道门类。具体状态类互相不相识;具体状态类仅仅从覆盖方法的参数中知晓当时所对应的门类的实例;
4. 每个具体的状态类可以采用单件方式(Singleton)实现;
上述解决方案的优势体现在:
1. 将状态和控制分开,两条线独立演化,系统的扩展性增强;
2. 状态之间互相不知晓,降低它们的耦合度;
3. 采用类名称以及事件名称,消除了原先的两层Case语句。
另外一个值得思考的问题:
状态的转换和动作的执行应该由谁来完成?在本例中,我认为状态类应该对此负责。因为如果由门类来完成,则该模式的应用就没有真正到位。
门类的职责:存储当前状态;转发事件;执行开门/关门动作;根据要求切换状态;
状态的真正切换还是由状态类来触发——从这个意义上来说,状态之间还是具有一定程度的耦合的——只是这种耦合不是直接的引用关系的耦合,而是通过逻辑关系进行的。
5 有关模式
模式是针对特定情况,适合解决特定场景下的特定问题的。一般而言,模式总能带来比较多的好处,但同时,也往往会带来一些缺陷或遗憾。如何取舍,要具体问题具体分析。其宗旨是如何让软件系统扩展性更好、可维护性更好,简单,容易理解。
6 其他 6.1 有关数据库持久化框架
本文地址:http://com.8s8s.com/it/it36773.htm