分类: LINUX
2010-12-28 22:39:00
l 问题描述:系统中的有些对象是由状态驱动的,它的行为与它的状态息息相关。一般这样的对象会被建模为状态机。当实现状态机的时候,无法避免要进行一系列的状态识别,从而引入一坨坨的if/else 或 switc/case,如果状态机的状态足够多,实现状态机的类将会变得相当的臃肿,并且代码的理解性会变得越来越差,当需求放生变化或增加新的功能时,修改这样的类变得非常的困难,更要命的是,对这样的类进行测试也非常的繁琐,尤其是修改状态类后进行回归也很头疼。前一阵子开发了一个系统,其中设计了一个状态机类,刚开始时只是使用了switch/case实现,后来增加了状态数量,也增加了逻辑,最后这个状态类充满了switch/case,维护起来越来越难了,所以进来学习了一下对状态机类的实现方法,今总结如下。
比如,针对游戏中的任务系统,任务可能拥有未接受、接受、拒绝、完成结束等状态,为方便讨论,暂且简化如下:
图1:task类的状态图
l 当对task 对象执行操作时,要经常的判断其状态到底满足不满足条件。以实现task对象为例,讨论一下状态机对象如何实现才能降低复杂度。比如对task 简单进行类设计如下:
图2:task 的类结构图
注:
这里忽略了task类的其他操作和属性,为简便讨论,我们只针对task类的Accept 和Reject 进行探讨即可。
刚开始的时候用如下的实现方式伪代码如下:
Function Accepted()
Bengin
switch status
Case Unaccpted:
{
Log("accept ok");
Return true;
}
Case Accpted:
{
Log("accept already");
Return false;
}
End
l 简单约定:
客户端:对状态类的对象的使用者,即会调用状态类对象的类。
主对象:被建模为状态机类的对象,这里就是task。
状态对象:只实现了状态机类的特定状态的行为,比如实现了一个主负责task在 Accepted状态下的操作的类,这种在此称为状态对象(在此就混淆一下类和对象 的概念啦)。
l 思路一:
利用状态对象:封装和状态相关的操作到特定的类中,无论何时客户端调用模态对象的某个行为,对象将方法分派给相应的状态类,当状态发生改变时,改变相应的状态对象即可。
类机构如下图:
图2:使用状态对象的类结构
l 示例代码
以Accept 接口为例:
Class Task_t:
int accept()
{
state_i* state = current_state();
if (true == state->handle_accept(task_data))
{
next_state();
}
}
Class state_unaccepted_t:
bool handle_accept()
{
return true;
}
bool handle_reject)
{
return false;
}
Class state_accepted_t:
bool handle_accept()
{
return false;
}
bool handle_reject)
{
return true;
}
l 优点:
将于状态相关的行为封装到特定的实现中,使得修改和扩展状态行为变得非常简单,状态类的实现是状态无关的,它接受主对象的实例数据作为参数。
l 缺点:
状态和状态相关的行为较少时,显得有点事倍功半,引入了不少类。
l 思路二:
有些场合会将相同的状态统一分类,当某一事件触发时,会引发这一类对象的相关操作。这种情况适合使用状态集合管理状态类。
l 伪代码示例:
Void do_xx()
Begin
##处于A状态的每个对象。
Foreach object in collection_state_A:
Object.do_xx()
collection_state_A.remove(Object)
collection_state_B.add(Object)
End