当对象的行为取决于一个或多个变化的属性,这样的属性叫做状态,对象状态是从事先定义好的一系列值中取出的。当对象与外部事件产生互动时,其内部状态就会改变,从而使得行为也随之发生变化。
实现步骤
步骤1.定义状态抽象类State
步骤2.定义操作状态类的上下文类Context
步骤3.具体状态类ConcreteState实现State的接口
步骤4.外部通过调用Context类实现状态变化的功能
应用实例
某施工队承包一座大楼的建设,施工队将大楼的建设分成四个阶段,监工负责监督施工队的进度。
- 打地基
- 建外墙
- 封顶
- 装修
使用状态模式模拟整个大楼的施工过程,外部(监工)无论何时何地都可以知道当前的施工进度。Phase类表示施工的各个阶段,Construct模拟施工过程。
第一步,定义抽象类Phase模拟施工进度。
class Construct;
class Phase {
public:
virtual void handle(Construct *c) = 0;
};
第二步,定义操作施工进度的类Construct,模拟施工动作。
class Construct {
public:
Construct() : m_day(0) {
m_phase = PhaseFoundation::Instance();
}
virtual ~Construct() {}
void changePhase(Phase * ph) {
m_phase = ph;
}
//询问当前的施工进度
void request(int day) {
setDay(day);
cout << "第" << day << "天: ";
m_phase->handle(this);
}
int getDay() { return m_day; }
private:
void setDay(int day) { m_day = day; }
Phase *m_phase; //施工阶段
int m_day; //工期
};
第三步,具体状态类实现Phase对施工的进度的控制。
//打地基
class PhaseFoundation : public Phase
{
public:
static Phase * Instance();
virtual ~PhaseFoundation() { delete m_phase; }
virtual void handle(Construct *c);
private:
PhaseFoundation() {}
static Phase *m_phase;
};
//建外墙
class PhaseOuterWall : public Phase
{
public:
static Phase * Instance();
virtual ~PhaseOuterWall() { delete m_phase; }
virtual void handle(Construct *c);
private:
PhaseOuterWall() {}
static Phase *m_phase;
};
//封顶
class PhaseCapping : public Phase
{
public:
static Phase * Instance();
virtual ~PhaseCapping() { delete m_phase; }
virtual void handle(Construct *c);
private:
PhaseCapping() {}
static Phase *m_phase;
};
//装修
class PhaseDecorate : public Phase
{
public:
static Phase * Instance();
virtual ~PhaseDecorate() { delete m_phase; }
virtual void handle(Construct *c);
private:
PhaseDecorate() {}
static Phase *m_phase;
};
Phase *PhaseFoundation::m_phase = NULL;
Phase *PhaseFoundation::Instance()
{
if (NULL == m_phase)
m_phase = new PhaseFoundation();
return m_phase;
}
void PhaseFoundation::handle(Construct * c) {
int day = c->getDay();
if (0 == day) {
cout << "施工开始,进入施工的第一阶段—打地基\n" << endl;
} else if (0 < day && day < 20) {
cout << "现在是施工的第一阶段—打地基, 需要工时20天\n" << endl;
} else if (20 == day) {
cout << "打地基工作完成,进入施工的第二阶段—建外墙\n" << endl;
c->changePhase(PhaseOuterWall::Instance());
}
}
Phase *PhaseOuterWall::m_phase = NULL;
Phase *PhaseOuterWall::Instance()
{
if (NULL == m_phase)
m_phase = new PhaseOuterWall();
return m_phase;
}
void PhaseOuterWall::handle(Construct * c) {
int day = c->getDay();
if (20 < day && day < 80) {
cout << "现在是施工的第二阶段—建外墙,需要工时60天\n" << endl;
}
else if (80 == day)
{
cout << "建外墙工作完成,进入施工的第三阶段—封顶\n" << endl;
c->changePhase(PhaseCapping::Instance());
}
}
Phase *PhaseCapping::m_phase = NULL;
Phase *PhaseCapping::Instance()
{
if (NULL == m_phase)
m_phase = new PhaseCapping();
return m_phase;
}
void PhaseCapping::handle(Construct * c)
{
int day = c->getDay();
if (80 < day && day < 110) {
cout << "现在是施工的第三阶段—封顶,需要工时30天\n" << endl;
}
else if (110 == day) {
cout << "封顶工作完成,进入施工的尾阶段—装修\n" << endl;
c->changePhase(PhaseDecorate::Instance());
}
}
Phase *PhaseDecorate::m_phase = NULL;
Phase *PhaseDecorate::Instance()
{
if (NULL == m_phase)
m_phase = new PhaseDecorate();
return m_phase;
}
void PhaseDecorate::handle(Construct * c) {
int day = c->getDay();
if (110 < day && day < 160) {
cout << "现在是施工的尾阶段—装修, 需要工时50天.\n" << endl;
} else if (160 == day) {
cout << "施工的尾阶段—装修工作完成,大楼交接.\n" << endl;
}
}
第四步,外部(监工)通过调用Construct类的询问接口询问当前的施工进度。
int main(int argc, char *argv[])
{
Construct * c = new Construct();
c->request(0);
c->request(10);
c->request(20);
c->request(50);
c->request(80);
c->request(95);
c->request(110);
c->request(130);
c->request(160);
delete c;
return 0;
}
运行结果:
优点
状态模式封装了转换规则,且给出了所有的状态,它将所有与某个状态有关的行为封装成具体的状态类,并且可以方便地增加这种具体类,只需要改变对象状态即可改变对象的行为。总之状态模式取代了if else语句,当需要新增加一种状态时,新增加一个状态类而不用去修改原有的代码,符合了设计模式的开闭原则。
缺点
缺点在于使用状态模式会增加系统类和对象的个数,且状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
和策略模式的对比
状态模式和策略模式非常相似,相似到长得一模一样!都是将主体Context(环境类)的一部分分离出来一个抽象类,再根据需要实现多个具体类,然后在主体中有选择的调用具体对象,这一过程其实就是多态咯。但是两中设计模式的意图还是很有区别的,一个是把操作对象的行为的状态封装起来,用外部行为操作内部;一个是封装一系列可以互相替换的实现方式。
状态模式解决的是当控制一个对象行为转换的条件表达式过于复杂的问题,它将各个状态的行为放到到一系列类中去实现,换来的是主体部分的简化,就像上面的实例,实际上所有的行为都可以在一个类的一个方法里面实现,也就是根据当前的工期天数改变施工的阶段,进而决定施工的行为。但是这样导致主体过于复杂臃肿,更何况建设一栋大楼何止需要四个施工阶段呢?
策略模式适用于需要一系列相同层次的具体类,这些类可以在主体中灵活的互相替换,当然替换的条件也来自于外部,而且也影响到了主体的行为。
总之,需要知晓两者的实现逻辑非常相似,但是适用的场景和设计初衷还是有很大的区别,要据此加以区分。