分享
 
 
 

大卫的Design Patterns学习笔记20:State

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

一、概述

State(状态)模式用于把一个对象的内部状态从对象中分离出来,形成单独的状态对象,所有与该状态相关的行为都放入该状态对象中。

一个对象可能处在这样或者那样的状态,并且在不同的状态下会表现出不同的行为,这是很平常的事情,例如,我们制作一个定点报时程序,当时间是0:00-12:00时,问候语是:“Good Morning! 现在是...AM.”;当时间是12:00-18:00时,问候语是:“Good Afternoon! 现在是...PM.”,其它时段,问候语是:“Good Evening! 现在是...PM.”,这只是一个简化的例子,你可以采用多种方式解决这个问题。

如以下是一种典型的实现:

void Alarm::Action()

{

int i = m_currentClock / 12;

if ( 0== i )

{

// Good Morning!

}

else

{

i = m_currentClock / 6;

if ( 2 == i)

{

// Good Afternoon!

}

else

{

// Good Evening!

}

}

}

看到这里,你可能会想:这样处理挺简单的呀,我就不信你用State模式的思想处理能比这更简单!

确实,对于上面的问题,由于十分简单,使用State模式并不能使解决它变得更加简单,但当需要执行的操作并非仅Action一个,而是多个,如Action1、Action2、....,上面的解决方案就会表现出一个明显的问题:所有的逻辑判断都必须被重复,任何“差池”都可能给我们的系统带来不易察觉的Bug,同时,维护也变得困难,任何需求的变更,如,我们要将11:00-12:00这个时间段的问候语改为:“Good Noon! 现在是...”,所有的逻辑判断部分都需要修改......

我们现在用State的观点来考虑这个问题,假定报时由Alarm类完成,Alarm对象在不同的时段处于不同的状态(Morning/Afternoon/Evening),为此,我们添加一个State基类,它的子类负责实现相应的报时动作。以上解决方案使得我们在整个系统运行的过程中,只需要维护状态的变化,而不是像前面采用if...else语句一样将所有的状态下的需要执行的行为混杂在一起,单纯依靠属性判断来决定执行何种动作。后面的举例部分将给出该问题的State模式实现。

State模式是OOD中用于替代if...else/switch的一种方式(当然,不仅限于此,应用部分对此有进一步讨论),State模式的应用使得对象被划分成对象 + 状态对象两部分,一定程度上体现了Delegate的设计原则。

在此需要注意的是,这里的State(状态)并非仅仅指对象当前所具有的属性,还包括对对象在拥有当前属性时行为的封装,这从类设计的角度讲是很自然的,因为类除了应该包括一定的属性信息,还应该包含操作这些属性的方法,及这些属性对应的行为。

二、结构

State模式的结构如下图所示:

图1、State模式类图示意

上述类图中包括如下角色:

Context(环境):负责定义客户感兴趣的接口,并维护一个ConcreteState的实例,以及维护状态转换或为这种转换提供支持(因为状态转换有时候可能是由上层应用发起的)。

State(状态):定义一个接口以封装与Context的一个特定状态相关的行为。

ConcreteState(具体状态):每一子类实现一个与Context的一个状态相关的行为。

三、应用

根据概述部分的讨论,可以看出,State模式适用于解决以下的问题:

1.一个对象的行为取决于它的状态, 并且它必须在运行时刻根据状态改变它的行为。

2.一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常, 有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化。

四、优缺点

State模式具有以下优点:

避免了为判断状态而产生的巨大的if或case语句。

将对象行为交给状态类维护后,对于上层程序而言,仅需要维护状态之间的转换规则。

并且,当状态信息发生增减时,维护成本比较低,仅需根据增减情况调整State子类的数目,并对状态转换逻辑进行简单调整,而不是陷入维护众多if...else逻辑判断的危险境地。

五、举例

运用State模式时存在一些细节问题需要进一步深入考虑,如:State类可能需要操作Context类的某些保护/私有成员以完成相应动作的执行,那么State类应该设置成Context类的friend类?或者内嵌类?或者其他形式?这里考虑到State扩展的需要,一般不会将State类设置成Context类的友元类(由于友元特性无法继承,主要是将State类的各子类设置成Context类的友元类),因为,不但所有State类都必须设置成Context的友元类,当需求发生变更,State发生增减时,都需要维护友元类信息。鉴于此,内嵌类是个不错的选择,但也并非唯一选择。

此外,State类是否需要维护一个Context类的指针等问题,也需要在具体实现时根据需要来确定,在此不再赘述。

下面给出概述中所讨论的定点报时问题的State模式解决方案:

#include <iostream>

using namespace std;

struct AlarmState

{

virtual ~AlarmState() { }

virtual void doAlarm() = 0;

virtual void changeState(AlarmState** ppState)

{

if (NULL != *ppState)

delete *ppState;

*ppState = NULL;

cout << "deleting..." << endl;

}

};

class MorningState : public AlarmState

{

public:

void doAlarm()

{

cout << "Good Morning!" << endl;

}

void changeState(AlarmState** ppState);

};

class NoonState : public AlarmState

{

public:

void doAlarm()

{

cout << "Good Noon!" << endl;

}

void changeState(AlarmState** ppState);

};

class AfternoonState : public AlarmState

{

public:

void doAlarm()

{

cout << "Good Afternoon!" << endl;

}

void changeState(AlarmState** ppState);

};

class EveningState : public AlarmState

{

public:

void doAlarm()

{

cout << "Good Evening!" << endl;

}

void changeState(AlarmState** ppState);

};

// To avoid problem, we seperate the implementation from the declaration.

// Otherwise, we must face a dilemma of the cyclic reference of ConcreteState

// classes.

void MorningState::changeState(AlarmState** ppState)

{

AlarmState::changeState(ppState);

*ppState = new NoonState;

}

void NoonState::changeState(AlarmState** ppState)

{

AlarmState::changeState(ppState);

*ppState = new AfternoonState;

}

void AfternoonState::changeState(AlarmState** ppState)

{

AlarmState::changeState(ppState);

*ppState = new EveningState;

}

void EveningState::changeState(AlarmState** ppState)

{

AlarmState::changeState(ppState);

*ppState = new MorningState;

}

class Alarm

{

AlarmState* pState;

public:

Alarm()

{

pState = new MorningState;

}

~Alarm()

{

if (pState != NULL)

delete pState;

}

void doAlarm()

{

pState->doAlarm();

}

void changeState()

{

pState->changeState(&pState);

}

};

int main()

{

Alarm a;

a.doAlarm();

// time elapses

a.changeState();

a.doAlarm();

//...

return 0;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有