目录
状态模式是一种行为型设计模式。当某个对象在不同状态下有不同行为时使用,在该模式中,我们将对象的不同状态
定义成不同的类,在这些状态类下有它们各自的行为,然后在客户端调用 Context 类,而不需要显示地去设置各个状态之间的转换
定义
- 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
该模式的本质是对象的状态改变时,会引起其行为的改变,从外部看就像该对象所对应的类的行为发生了改变一样。例如,电灯的开关,在灯的不同状态下(亮或灭),按下开关时,灯的行为是不一样的。或者说汽车,只有在车停下的时候才能打开车门,行驶的时候是不可以开门的。这就是状态的不同就会导致不同的行为。
- 从UML来看,UML的结构似乎和策略模式是一模一样的,但是两种模式的本质却是完全不同的。策略模式中的策略(或者说是行为)是因为根据外界需要而使用不同的策略来完成需求;而状态模式中,是因为其自身状态改变,而影响了自身的一系列行为的改变。虽然这个状态的变更可能是外界引发的。
- 抽象状态角色(State),负责定义各个状态有哪些行为,该抽象状态包含所有具体状态的方法。并且封装环境角色,帮助切换状态。
- 具体状态角色(ConcreteState),实现具体状态下可以执行的行为, 每一个具体状态类必须完成两个职责:该类本状态下要做的事情,以及如何执行到其他具体状态类的状态。
- 环境角色(Context) ,该角色是客户端要调用的接口,在该类内部维护一个ConcreteState子类的一个实例,可以负责具体状态的切换(客户端不知道状态的切换)。
用C++ 代码实现大话设计模式本章代码:
#include<iostream>
using namespace std;
class State;
class ForenoonState;
class NoonState;
class AfternonnState;
class EveningState;
class SleepingState;
class RestState;
class Work;
// 抽象状态角色,负责定义各个状态有哪些行为,该抽象状态包含所有具体状态的方法。并且封装环境角色,帮助切换状态。
class State
{
public:
virtual void writeprogram(Work* w) = 0;
virtual ~State() = default;
};
// Contex 角色,在该类内部维护一个ConcreteState子类的一个实例,可以负责具体状态的切换
class Work
{
private:
State* current;
double m_hour;
bool m_finish = false;
public:
Work();
~Work();
void setHour(double hour) { m_hour = hour; }
double getHour() { return m_hour; }
void setFinish(bool finish) { m_finish = finish; }
bool getFinish() { return m_finish; }
void SetState(State* s)
{
delete current;
current = s;
}
void WorkProgram()
{
current->writeprogram(this);
}
};
// 下面都是具体的状态类,每一个具体状态类必须完成两个职责:该类本状态下要做的事情,以及如何执行到其他具体状态类的状态。
class ForenoonState :public State
{
public:
void writeprogram(Work* w)override;
};
class NoonState :public State
{
public:
void writeprogram(Work* w)override;
};
class AfternoonState :public State
{
public:
void writeprogram(Work* w)override;
};
class EveningState :public State
{
public:
void writeprogram(Work* w)override;
};
class SleepingState :public State
{// 睡眠状态
public:
void writeprogram(Work *w)override;
};
class RestState :public State
{// 下班休息状态
public:
void writeprogram(Work *w)override;
};
Work::Work()
{
current = new ForenoonState();
}
Work::~Work()
{
delete current;
}
void ForenoonState::writeprogram(Work* w)
{
if (w->getHour() < 12)
{
cout << "当前时间:" << w->getHour() << "点,上午工作,精神百倍" << endl;
}
else
{
w->SetState(new NoonState());
w->WorkProgram();
}
}
void NoonState::writeprogram(Work* w)
{
if (w->getHour() < 13)
{
cout << "当前时间:" << w->getHour() << "点,吃午饭,睡午觉" << endl;
}
else
{
w->SetState(new AfternoonState());
w->WorkProgram();
}
}
void AfternoonState::writeprogram(Work* w)
{
if (w->getHour() < 17)
{
cout << "当前时间:" << w->getHour() << "点,下午状态还不错" << endl;
}
else
{
w->SetState(new EveningState());
w->WorkProgram();
}
}
void EveningState::writeprogram(Work* w)
{
if (w->getFinish())
{
w->SetState(new RestState());
w->WorkProgram();
}
else
{
if (w->getHour() < 21)
{
cout << "当前时间:" << w->getHour() << "点,加班哦,疲惫至极 " << endl;
}
else
{
w->SetState(new SleepingState());
w->WorkProgram();
}
}
}
void SleepingState::writeprogram(Work *w)
{
cout << "当前时间:" << w->getHour() << "点,不行了,睡着了 " << endl;
}
void RestState::writeprogram(Work *w)
{
cout << "当前时间:" << w->getHour() << "点,下班回家了 " << endl;
}
int main()
{
Work emergencyProjects;
emergencyProjects.setHour(9);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(10);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(12);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(13);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(14);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(17);
emergencyProjects.WorkProgram();
emergencyProjects.setFinish(false);
emergencyProjects.setHour(19);
emergencyProjects.WorkProgram();
emergencyProjects.setHour(22);
emergencyProjects.WorkProgram();
system("pause");
return 0;
}
运行后截图:
可以看到,在客户端代码中,我们只需要调用Context类中方法即可,并不需要手动去改变状态,每执行一次方法就可以自动改变对象的状态,进而可以产生不同的行为,这就是状态模式。
优点
- 通过把各种状态判断逻辑分布到State的子类中,减少相互之间的依赖
在状态模式中,环境(Context)中不必出现大量的条件判断语句。环境(Context)实例所呈现的状态变得更加清晰、容易理解,提高代码的可维护性。
- 如果不是有状态模式,一个对象需要在不同状态执行不同方法或者拥有不同特性的话,你可能需要在大量的 switch...case 或者 if...else 语句来根据不同状态执行不同方法,代码看起来就很复杂了
遵循设计原则
- 每一个状态都是一个子类,符合单一职责原则,且当需要增加一个状态时,也是可以增加一个子类,需要修改一个状态时,也是修改一个子类即可。
- 所有的状态都是一个子类,如果修改状态,只需要修改这个状态的子类即可,符合单一职责原则。且当需要增加一个状态时,也增加一个子类即可;需要修改一个状态时,也是修改一个子类即可。
封装性好
- 状态的变换放置在类的内部实现,外界调用不知道内部的状态改变,只要调用其方法即可。
缺点
- 由于所有的状态都是一个类,有的对象可能会有非常多的状态。随着状态的增加,系统类和对象个数也会增加。这个时候就使用状态模式就会导致类特别多,结构和实现会比较复杂,且不利于维护。如果使用不当将导致程序结构和代码的混乱。所以说,在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
- 状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式。
- 需要编写大量的条件分支语句来决定一个操作的行为,而且这些条件恰好表示对象的一种状态。