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

news2024/12/23 23:58:05

状态模式,就是把所有的状态抽象成一个个具体的类,然后继承一个抽象状态类,在每一个状态类内封装对应状态的行为,符合开放封闭原则,当增加新的状态或减少状态时,只需修改关联的类即可。很适合多分支行为方法的处理,这里的多分支,当然是状态比较多的情况下,如果只有小于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.coloradmin.cn/o/600369.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot使用flywaydb实现数据库版本管理【附源码】

一、项目背景 本文主要是配合SpringBoot使用用户输入的自定义数据源启动一文附带产出。前文主要介绍了SpringBoot无数据源启动&#xff0c;然后通过用户录入自定义数据库配置后&#xff0c;连接数据库的操作。但是当整个项目交给用户使用时&#xff0c;谁使用都不知道情况下&a…

JetBrains的.NET和ASP.NET集成开发环境Rider 2023版本在Linux系统的下载与安装配置教程

目录 前言一、Rider 安装二、使用配置总结 前言 Rider是一款专为.NET和ASP.NET开发人员设计的集成开发环境&#xff08;IDE&#xff09;。它提供了丰富的功能和工具&#xff0c;可以帮助开发人员更高效地编写、调试和部署.NET和ASP.NET应用程序。注&#xff1a;已在CentOS7.9和…

Java课程设计之购物车管理系统

一、项目准备 1、开发工具&#xff1a;JDK、Eclipse 2、需求分析&#xff1a; 包括商品管理和购物车管理。 1&#xff09;商品管理主要功能 商品信息导入 显示所有商品信息 2&#xff09;购物车主要功能 添加商品到购物车 修改购物车中的商品数量 显示购物车中的所有商…

运维小白必学篇之基础篇第六集:权限实验

权限实验 实验作业&#xff1a; 1、创建1.txt文件&#xff0c;修改1.txt文件权限为属主最大&#xff0c;属组读写&#xff0c;其他人无权限 2、单独为1.txt文件的属组赋予执行权限 3、修改1.txt的属组为a1 4、修改用户a2的登录shell为/bin/bash 5、创建a1用户&#xff0c;设置…

chatgpt赋能python:Python内置函数求和

Python内置函数求和 Python是一种易学易用的编程语言&#xff0c;是许多开发人员和数据分析师的首选语言。Python提供了多种内置函数来处理不同的任务&#xff0c;其中包括求和函数。本文将介绍Python中的内置求和函数以及如何使用它们。 Python内置求和函数 在Python中&…

【51单片机】AT24C20数据帧(I2C总线)

&#x1f38a;专栏【51单片机】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Love Story】 &#x1f970;大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 小吉先向大家道个歉&#xff0c;因为最近在期末…

“微商城”项目(4首页)

1.显示轮播图 首页和商品详情页都有图片轮播图展示&#xff0c;考虑到Vue组件代码的复用性&#xff0c;把轮播图相关代码单独放置在src\components\swiper.vue文件中。 在src\pages\Home.vue文件中&#xff0c;编写HTML结构代码&#xff0c;示例代码如下。 <template>…

大数据AI课程更新——6月AI绘画入门小课

在这个课程中&#xff0c;我们将探索人工智能在绘画领域的应用&#xff0c;学习如何利用AI技术创造出令人惊叹的艺术作品。无论你是对绘画有兴趣的初学者&#xff0c;还是已经有一定绘画基础的学生&#xff0c;本训练营都将为你提供一个展示创造力和实践技巧的平台。 2022年是A…

永远年轻,永远在路上的AI TIME

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 在2019年的智源人工智能大会上&#xff0c;我第一次接触AI TIME&#xff0c;时至今日&#xff0c;加入AI TIME已经四年了。在过去的四年里&#xff0c;AI技术、产业发生了翻天覆地的变化&#xff0c;我自己的思…

80后程序员涛哥的童年

博主&#xff1a;爱码叔 个人博客站点&#xff1a; icodebook 公众号&#xff1a;漫话软件设计 微博&#xff1a;程序员涛哥 专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上&#xff0c;欢迎大家…

态路小课堂丨交换机堆叠—简化组网结构,增强网络可靠性

TARLUZ态路 01、什么是交换机堆叠 交换机堆叠是指将多台支持堆叠特性的交换机通过堆叠线缆连接起来&#xff0c;从逻辑上虚拟成一台交换设备&#xff0c;该交换机中的所有交换机共享相同的配置信息和路由信息。当向逻辑交换机增加和减少单体交换机时不会影响其性能。 02、有什么…

新鲜出炉

最近发现了一爆款APP重新来袭&#xff0c;心中不由感叹“经典永不过时”&#xff01;它可是能节省真金白银的&#xff0c;尤其是几年前&#xff0c;流量真是不便宜&#xff0c;出门到了哪里都是赶紧寻找wifi和密码&#xff0c;直到它的出现&#xff0c;可以完全自动化&#xff…

除了降价,阿里云还有王炸

1. 写本文的前因 最近业内多家云厂商都找到了简单的宣传方法&#xff0c;那就是跟风降列表价。我并不反对这种常规宣传&#xff0c;我那篇科普谈降价的文章&#xff0c;据说也让某些云参透了敲锣不要钱&#xff0c;宣传没损失。 在阵阵锣声中&#xff0c;我随口和朋友们发牢骚&…

盘点一个Python自动化办公需求,实现数据自动填充(上篇)

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 临别殷勤重寄词&#xff0c;词中有誓两心知。 大家好&#xff0c;我是皮皮。 一、前言 前几天遇到了一个小需求&#xff0c;粉丝自己在实际工作中的需求…

牛叉,一行代码不写,就可以开发系统

如今AI和低代码越来越火&#xff0c;可以瞬间完成一个系统的开发。 不用一行代码&#xff0c;轻松实现业务数字化&#xff0c;是怎么做到的&#xff1f; 前面小孟开发了大量的系统&#xff0c;很多时候不是我写代码多么快&#xff0c;也不是我技术多么的厉害&#xff0c;而是…

GPU与CPU

1 什么是GPU&#xff1f; GPU的英文全称Graphics Processing Unit&#xff0c;图形处理单元。通俗来说&#xff0c;GPU是一款专门的图形处理芯片&#xff0c;做图形渲染、数值分析、金融分析、密码破解&#xff0c;以及其他数学计算与几何运算的。GPU可以在PC、工作站、游戏主…

chatgpt赋能python:Python如何关闭对话框?

Python如何关闭对话框&#xff1f; 如果你是一名Python编程经验丰富的工程师&#xff0c;你一定已经遇到过需要在Python中关闭对话框的场景。在这篇文章中&#xff0c;我将详细介绍如何处理这个问题&#xff0c;并提供一些有用的技巧和建议。 什么是对话框&#xff1f; 在编…

跨境电商平台支持后台采集商城PC+WAP+小程序+APP,Java

跨境电商系统概述 跨境电商系统是一种在全球范围内开展电子商务的技术平台。它能够帮助企业建立贸易关系、实现商品销售、支付和物流配送等功能&#xff0c;提高企业的市场竞争力和盈利能力。该系统支持自营和招商入驻的电商模式&#xff0c;多终端交互&#xff0c;有PC、APP、…

【Springboot实用功能开发】发送QQ邮件以及邮件验证码对话框

文章目录 参考博客1. 发送一封QQ邮件添加Maven依赖QQ邮箱开启POP服务配置application.properties文件Controller层编写 vue前端&#xff08;也可以直接省略使用PostMan测试接口&#xff09; 2. 做一个发送邮件验证的对话框 参考博客 vue实现验证码倒计时功能SpringBoot实现邮箱…

会讲ppt的技术人,平均薪资多30%以上!

一场好的技术分享&#xff0c;可以用“有趣有料”四个字来形容&#xff0c;那么如何才能做到有趣又有料呢&#xff1f;结合我的经历&#xff0c;做了一些总结。 2015年&#xff0c;我出版《技术管理之巅》以后&#xff0c;先后收到QCon、CSDN、IT168等业界知名技术大会的邀请担…