二十三种设计模式:状态模式

news/2024/11/8 0:35:42/

状态模式,就是把所有的状态抽象成一个个具体的类,然后继承一个抽象状态类,在每一个状态类内封装对应状态的行为,符合开放封闭原则,当增加新的状态或减少状态时,只需修改关联的类即可。很适合多分支行为方法的处理,这里的多分支,当然是状态比较多的情况下,如果只有小于4个状态,个人认为还是分支处理简单些。

状态模式正规的定义与类图(引用《大话设计模式》)如下所示:![[Pasted image 20230413215739.png]]![[Pasted image 20230413215745.png]]
在这里插入图片描述

这里以一天工作中的工作状态为例实现状态模式。
工作状态的类图结构(引用《大话设计模式》)如下所示:在这里插入图片描述

C++代码实现如下:

#include <iostream>
#include <memory>//*********************State Pattern*******************
class Work;//抽象状态类
class State
{
public:virtual void WriteProgram(Work* ptrWork) = 0;
};class ForenoonState;//工作类
class Work
{
private:std::shared_ptr<State> smartState;double hour;bool finish;public:Work();void SetHour(const double h){hour = h;}double GetHour() const{return hour;}void SetFinish(bool bFinish){finish = bFinish;}bool GetFinish() const{return finish;}void SetState(std::shared_ptr<State> pState){smartState = pState;}std::shared_ptr<State> GetState() const{return smartState;}void WriteProgram(){smartState->WriteProgram(this);}};//睡眠状态
class SleepingState : public State
{
public:void WriteProgram(Work* ptrWork){std::cout << "当前时间:" << ptrWork->GetHour() << "点 撑不住了,睡觉吧!" << std::endl;}
};//下班休息状态
class RestState : public State
{
public:void WriteProgram(Work* ptrWork){std::cout << "当前时间:" << ptrWork->GetHour() << "点 下班回家了!" << std::endl;}
};//傍晚工作状态
class EveningState : public State
{
public:void WriteProgram(Work* ptrWork){if (ptrWork->GetFinish()){ptrWork->SetState(std::make_shared<RestState>());ptrWork->WriteProgram();return;}if (ptrWork->GetHour() < 21){std::cout << "当前时间:" << ptrWork->GetHour() << "点 加班吆,疲惫啊!" << std::endl;}else{//超过21点,转换到睡眠状态ptrWork->SetState(std::make_shared<SleepingState>());ptrWork->WriteProgram();}}
};//下午工作状态
class AfternoonState : public State
{
public:void WriteProgram(Work* ptrWork){if (ptrWork->GetHour() < 17){std::cout << "当前时间:" << ptrWork->GetHour() << "点 下午状态还不错,继续努力!" << std::endl;}else{//超过17点,转换傍晚工作状态ptrWork->SetState(std::make_shared<EveningState>());ptrWork->WriteProgram();}}
};//中午工作状态
class NoonState : public State
{
public:void WriteProgram(Work* ptrWork){if (ptrWork->GetHour() < 13){std::cout << "当前时间:" << ptrWork->GetHour() << "点 饿了,午饭,犯困,午休!" << std::endl;}else{//超过13点,转换下午工作状态ptrWork->SetState(std::make_shared<AfternoonState>());ptrWork->WriteProgram();}}
};//上午工作状态类
class ForenoonState : public State
{
public:void WriteProgram(Work* ptrWork){if (ptrWork->GetHour() < 12){std::cout << "当前时间:" << ptrWork->GetHour() << "点 上午工作,精神百倍!" << std::endl;}else{//超过12点,转换中午工作状态ptrWork->SetState(std::make_shared<NoonState>());ptrWork->WriteProgram();}}
};Work::Work() : hour(0), finish(false), smartState(std::make_shared<ForenoonState>()) {}//************************Test**************************
int main()
{std::shared_ptr<Work> work = std::make_shared<Work>();work->SetHour(9);work->WriteProgram();work->SetHour(10);work->WriteProgram();work->SetHour(12);work->WriteProgram();work->SetHour(13);work->WriteProgram();work->SetHour(14);work->WriteProgram();work->SetHour(17);work->WriteProgram();work->SetFinish(false);//work->SetFinish(true);//work->WriteProgram();work->SetHour(19);work->WriteProgram();work->SetHour(22);work->WriteProgram();system("pause");return 0;
}

实际题例:

题例:万能糖果公司
我们认为糖果机的控制器需要如下图般的工作,希望你能用C++语言帮我们实现它,而且需要让设计能够尽量有弹性而且好维护,因为将来我们可能要为它增加更多的行为。 ![[Pasted image 20230413215937.png]]

这张图是一个状态图,糖果机的所有状态有:“没有25分钱”,“有25分钱”,“糖果售罄”,“售出糖果”,每一个状态都代表机器不同的配置,需要某些动作将目前的状态转换到另外一个状态,要进入另外一种状态,必须做某些事情。对于糖果机当前任何一个动作,我们都需要检查,看看糖果机所处的状态和动作是否合适。

下面我们按照一般思维来设计糖果机的这些功能,步骤如下:
(1)找出所有的状态
没有25分钱”、“有25分钱”、“糖果售罄”、“售出糖果
(2)创建一个实例变量来持有目前的状态,然后定义每个状态的值

const static int NO_QUARTER = 1;
const static int HAS_QUARTER = 2;
const static int SOLD_OUT = 0;
const static int SOLD = 3;
int state = SOLD_OUT;

3)将所有糖果机系统中可以发生的动作整合起来
根据状态图可知,四个状态对应着四个动作:“投入25分钱”,“退回25分钱”,“转动曲柄”,“发放糖果”;
这些动作是糖果机的对客户的接口,糖果机会以机器按钮的方式让这些接口与玩家交互,这是你能对糖果机做的事情,调用任何一个动作都会造成状态的转换,发放糖果更多是糖果机的内部动作,机器自己调用自己。
(4)现在,我们创建了一个类,它的作用就像是一个状态机,对每一个动作,我们都创建了一个对应的方法,这些方法利用条件语句来决定在每个状态内什么行为是恰当的。每一个可能的状态都需要用条件语句检查,然后对每一个可能的状态展现适当的行为。

#include<iostream>
using namespace std;
class GumballMachine {
private:const static int SOLD_OUT = 0;    /* 售罄状态 */const static int NO_QUARTER = 1;  /* 没有25分钱状态 */const static int HAS_QUARTER = 2; /* 有25分钱状态 */const static int SOLD = 3;        /* 售出状态 */int state = SOLD_OUT;			  /* 糖果机拆箱状态初始化为售罄状态,等待工人装入糖果 */int count = 0;                    /* 糖果机当前糖果数量 */
public:/* 构造函数初始化,装入糖果后,就需要改变糖果机当前状态为 "没有25分钱状态" */GumballMachine(int count) {this->count = count;if (count > 0) {state = NO_QUARTER; }}/* 投入25分钱 */void insertQuarter() {if (state == HAS_QUARTER) {cout << "You can't insert another quarter" << endl;} else if (state == NO_QUARTER) {state = HAS_QUARTER; /* 只有当前状态为无25分钱状态才能执行投币操作 */cout << " You inserted a quarter"<<endl;} else if (state == SOLD_OUT) {cout << "You can't insert a quarter, the machine is sold out" <<endl;} else if (state == SOLD) {cout << "Please wait, we're already giving you a gumball" << endl;} }/* 退回25分钱 */void ejectQuarter() {if (state == HAS_QUARTER) {cout<< "Quarter returned" <<endl;state = NO_QUARTER;} else if (state == NO_QUARTER) {cout << "You haven't inserted a quarter, we can't return quarter to you" << endl;} else if (state == SOLD) {cout << "you already turned the crank, we can't return quarter to you" << endl;} else if (state == SOLD_OUT) {cout << "You can't eject,you haven't inserted a quarter"<<endl;}	}/* 转动曲柄 */void turnCrank() {if (state == HAS_QUARTER) {cout << "You turned the crank..." << endl;state = SOLD;dispense();} else if (state == SOLD_OUT) {cout<< "You turned, but there is no gumball" <<endl;} else if (state == SOLD) {cout << "Turning twice doesn't get you another gumball!"<<endl;} else if (state == NO_QUARTER){cout<<"You turned, but there is no quarter"<<endl;}}/* 发放糖果 */void dispense(){if (state == HAS_QUARTER){cout<<"No gumball dispensed"<<endl;} else if (state == SOLD_OUT) {cout << "No gumball dispensed" << endl;} else if (state == SOLD) {cout<< "A gumball comes rolling out the slot"<<endl;count--;if (count == 0) {cout<<"Oops, Out of gumball! "<<endl;state = SOLD_OUT;} else {state = NO_QUARTER;}} else if (state == NO_QUARTER) {cout <<"You need to pay first"<<endl;}}
};

上述代码if-else多么的可怕,主要是如果糖果机器以后状态增多,那么修改if-else将是一个巨大的工作量,如果采用状态模式,在动作发生时委托给当前状态,将是一个不错的选择,主要分为以下3个步骤:
(1)定义一个State接口,在这个接口内,糖果机的每个动作都有一个对应的方法;
(2)为机器的每个状态实现状态类,这些状态类负责在对应的状态下进行机器的行为;
(3)摆脱老代码束缚,取而代之的方式将动作委托到状态类。

完整代码见下:

class GumballMachine;
/* 抽象接口,在此接口内,糖果机的每个动作都有一个对应的方法 */
class State{
public:virtual void insertQuarter() = 0; /* 投入25分钱 */virtual void ejectQuarter() = 0;  /* 退回25分钱 */virtual void turnCrank() = 0;     /* 转动曲柄 */virtual void dispense() = 0;      /* 发放糖果 */
};/* 没有25分钱状态:根据状态图,当前状态只能执行"投入25分钱"的动作 */
class NoQuarterState:public State {
private:GumballMachine *gumballMachine;
public:NoQuarterState(GumballMachine *gumballMachine) {this->gumballMachine = gumballMachine;}/* 当前状态下,可以投币,机器状态根据状态图改变 */void insertQuarter() {cout << "You inserted a quarter" << endl;gumballMachine->setState(gumballMachine->getHasQuarterState());}/* 不恰当操作 */void ejectQuarter() { cout << "You have not inserted a quarter"<<endl;}/* 不恰当操作 */void turnCrank() { cout << "You turned,but there is no quarter" << endl;}/* 不恰当操作 */void dispense() {cout << "You need to pay quarter first."<<endl;}
};/* 有25分钱状态:根据状态图,当前状态可以执行"退回25分钱"和"转动曲柄"的动作 */
class HasQuarterState:public State {
private:GumballMachine *gumballMachine;
public:HasQuarterState(GumballMachine *gumballMachine) {this->gumballMachine = gumballMachine;}/* 不恰当操作 */void insertQuarter() {cout << "You can't insert another quarter." << endl;}/* 当前状态下,可以退出25分钱,状态需要根据状态图改变 */void ejectQuarter() {cout << "Quarter returned" << endl;gumballMachine->setState(gumballMachine->getNoQuarterState());}/* 当前状态下,可以转动曲柄,状态需要根据状态图改变 */void turnCrank() {cout << "You turned the Crank..." << endl;gumballMachine->setState(gumballMachine->getSoldState());}/* 不恰当操作 */void dispense() {cout << "No gumball disoensed"<<endl;}
};/* 售出糖果状态:根据状态图,当前状态只能执行"发放糖果"的动作 */
class SoldState:public State {
private:GumballMachine *gumballMachine;
public:SoldState(GumballMachine *gumballMachine) {this->gumballMachine = gumballMachine;}/* 不恰当操作 */void insertQuarter() {cout << "Pleaae wait, we're already giving you a gunball" << endl;}/* 不恰当操作 */void ejectQuarter() {cout << "Sorry, you aleady turned the crank." << endl;}/* 不恰当操作 */void turnCrank() {cout << "Turning twice doesn't get you another gunball" << endl;}/* 当前状态可以放糖果 */void dispense() {gumballMachine->releaseBall();if (gumballMachine->getCount() > 0){gumballMachine->setState(gumballMachine->getNoQuarterState());} else {gumballMachine->setState(gumballMachine->getSoldState());}}
};/* 售罄状态:根据状态图,当前状态都不能执行,只有等着人员给机器装糖果 */
class SoldOutState:public State {
private:GumballMachine *gumballMachine;
public:SoldOutState(GumballMachine *gumballMachine) {this->gumballMachine = gumballMachine;}void insertQuarter() {cout << "you can't insert a quarter, the machine is sold out" << endl;}void ejectQuarter() {cout << "you can't insert a quarter, you have not inserted a quarter yet" << endl;}void turnCrank() {cout << "you turned, but there no gumballs" << endl;}void dispense() {cout << "No gumball dispensed" << endl;}
};class GumballMachine{
private:State *soldOutState;State *noQuarterState;State *hasQuarterState;State *soldState;State *state = soldOutState;int count = 0;
public:GumballMachine(int count) {soldOutState = new SoldOutState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);soldOutState = new SoldState(this);this->count = count;if (count > 0) {state = noQuarterState;//糖果机初始状态}}void insertQuarter() {state->insertQuarter();}void ejectQuarter() {state->ejectQuarter();}void turnCrank(){state->turnCrank();state->dispense();}void setState(State *state){this->state = state;}void releaseBall(){cout << "A gumball comes rolling out the slot"<<endl;if (count != 0){count = count - 1;}}State* getSoldState() {return soldState;}State* getHasQuarterState() {return hasQuarterState;}State* getNoQuarterState() {return noQuarterState;}State* getSoldOutState() {return soldOutState;}int getCount(){return count;}
};

四、状态模式分析

(1)状态模式描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为。
(2)状态模式的关键是引入了一个抽象类来专门表示对象的状态,这个类我们叫做抽象状态类,而对象的每一种具体状态类都继承了该类,并在不同具体状态类中实现了不同状态的行为,包括各种状态之间的转换。

五、适用场景

(1)行为随状态改变而改变的场景。
(2)条件、分支语句的代替者。

六、模式优点

(1)实现多态行为的好处是显而易见的,并且很容易添加状态来支持额外的行为。
(2)在状态模式中,对象的行为是其状态中函数的结果,并且在运行时根据状态改变行为,这就消除了对switch/case 或 if/else 条件逻辑的依赖。
(3)可以提高内聚性,因为状态特定的行为被聚合到具体的类中,这些类被放在代码中的一个位置。


http://www.ppmy.cn/news/163163.html

相关文章

华清远见 day04

break 打破循环,再也不执行 continue 跳出本次循环,继续执行下一次循环; ​ 常量 字面常量 宏常量 #define A 100 //定义一个宏常量, 名为:A 值为:100 位置 在 头文件 下面 ,文件开头 ​ ​ 输入时间秒 得到 小时 分钟 秒的时间输出 用到 三运算符; 宏常量 Mi 是60 t1 /Mi>6…

WordPress主题Modown_v8.7主题免授权+Erphpdown15.21+团购+第三方登录

团购内置在主题里面了已不需要安装插件了,官方带免费子主题,主题无需授权和其他操作,安装启用即可免授权使用 主题简介 Modown是模板兔基于Erphpdown wordpress下载插件开发的一款全新的针对收费付费下载资源/付费查看内容/VIP会员免费下载查看/虚拟资源售卖的WordPress主题…

笔记本外接显示器屏幕分辨率调节(亲测、实用)

1、右击笔记本主屏&#xff0c;打开显示设置 2、高级显示设置 3、点击显示2的适配器属性 4、列出所有模式 5、选择如下分辨率 大体上分辨率都是这个&#xff0c;或在周围波动&#xff0c;选择让显示器自适应的一项即可

计算机显示器分辨率,电脑分辨率多少合适,详细教您电脑显示器分辨率怎么调整...

电脑屏幕分辨率是指屏幕显示出来的图像精密度&#xff0c;由于屏幕上的点、线和面都是由像素组成的&#xff0c;显示器可显示的像素越多&#xff0c;画面就越精细&#xff0c;同样的屏幕区域内能显示的信息也越多&#xff0c;所以分辨率是个非常重要的性能指标之一。下面&#…

服务器 分辨率问题 显示器不显示不出来,显示器没有最佳分辨率及分辨率调不了的解决方法...

之前我们详细介绍了各尺寸液晶显示器的最佳分辨率。但有些朋友遇到了这样的问题&#xff1a;显示器没有最佳分辨率或者分辨率调不了。比如说19寸的显示器最佳分辨率是1440900&#xff0c;可是他的分辨率设置里边却找不到1440900这一项。 那么是什么原因导致的呢&#xff1f;一般…

中小尺寸常见显示屏分辨率列表

随着显示制造技术的突飞猛进&#xff0c;新的工艺的日新月异&#xff0c;显示屏的分辨率一而再再而三的突破极限&#xff0c; 诸多分辨率不同的显示屏逐渐走入日常生活中&#xff0c;下面收集了一些常见的分辨率的显示尺寸列表&#xff1a; VGA 分辨率 640*480 SVGA 分辨率 80…

茅塞顿开的C#代码——通用型科学计算器

计算器是经常遇到的编程作业。 一般都是实现加、减、乘、除四则运算的普通计算器。 这里介绍用几十行C#代码实现的复杂的《科学计算器》&#xff0c;可以计算各种函数。 不知道其他语言实现同样的功能需要编写多少行代码&#xff1f;20000行&#xff1f; using System; usin…

常见的电脑屏幕分辨率统计

通常情况下 &#xff0c;屏幕的分辨率月越高&#xff0c;所包含的像素也就越多&#xff0c;图片就越清晰。   根据不同笔记本、台式电脑以及Mac电脑屏幕大小以及分辨率都有所不同&#xff0c;从网上摘要对屏幕常见屏幕分辨率的归纳进行对比和开发尺寸使用。 笔记本电脑各大尺…