状态模式——C++实现

目录

1. 状态模式简介

2. 代码示例

3. 单例状态对象

4. 状态模式与策略模式的辨析


1. 状态模式简介

状态模式是一种行为型模式

状态模式的定义:状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

通俗的说就是一个对象在不同的状态下拥有不同的行为。对象可以拥有多个不同的状态,不同状态下调用同一个接口会产生不同的行为。状态模式通过把状态封装成类,可以很好地维护一个对象的不同状态,并且方便地扩展新的状态。


举个例子:

假如在游戏程序中,要模拟一个怪物。怪物的总血量为100,当血量大于50时,怪物处于亢奋状态,怪物受击时会进行反击;当血量小于等于50时,怪物处于恐慌状态,受击时会选择逃跑。

在这个例子中,怪物具有明确的状态,并且在不同的状态下,同一个接口会有不同的行为。这种情况下就非常适合使用状态模式。

2. 代码示例

下面的代码实现了上文提到的怪物模拟程序:

#if 1

#include <iostream>

using namespace std;

class Monster;
class State
{
protected:
	Monster* monster_;
public:
	virtual ~State() {}
	State(Monster* m) :monster_(m) {}
	virtual void attacked(int damage) = 0;
};



class Monster
{
public:
	Monster();
	~Monster();
	void setState(State* state)
	{
		curState = state;
	}
	State* getExcitedState()
	{
		return excitedState;
	}
	State* getHorrifiedState()
	{
		return horrifiedState;
	}
	State* getDeadState()
	{
		return deadState;
	}
	void attacked(int damage)
	{
		curState->attacked(damage);
	}
	int getCurHealth()const { return health; }
	int reduceHealth(int damage)
	{
		health -= damage;health = max(0, health);
		return health;
	}
private:
	State* curState;
	State* excitedState;
	State* horrifiedState;
	State* deadState;
	int health = 100;
};

class ExcitedState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 ExcitedState" << endl;
		cout << "怪物尝试反击" << endl;
		cout << "怪物受到" << damage << "点伤害" << endl;
		int curHealth = monster_->reduceHealth(damage);
		cout << "怪物剩余血量:" << curHealth << endl;
		if (curHealth <= 50)
		{
			monster_->setState(monster_->getHorrifiedState());
			cout << "怪物进入 HorrifiedState" << endl;
		}
	}
};

class HorrifiedState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 HorrifiedState" << endl;
		cout << "怪物尝试逃跑" << endl;
		cout << "怪物受到" << damage << "点伤害" << endl;
		int curHealth = monster_->reduceHealth(damage);
		cout << "怪物剩余血量:" << curHealth << endl;
		if (curHealth <= 0)
		{
			monster_->setState(monster_->getDeadState());
			cout << "怪物进入 DeadState" << endl;
		}
	}
};

class DeadState : public State
{
public:
	using State::State;
	void attacked(int damage)
	{
		cout << "怪物的当前状态为 DeadState" << endl;
		cout << "怪物已经死亡, 无法受到伤害" << endl;
	}
};

Monster::Monster()
{
	excitedState = new ExcitedState(this);
	horrifiedState = new HorrifiedState(this);
	deadState = new DeadState(this);
	curState = excitedState;
}

Monster::~Monster()
{
	delete excitedState;
	delete horrifiedState;
	delete deadState;	
}


int main()
{
	Monster monster;
	monster.attacked(0);
	cout << "---------------------" << endl;
	monster.attacked(30);
	cout << "---------------------" << endl;
	monster.attacked(40);
	cout << "---------------------" << endl;
	monster.attacked(80);
	cout << "---------------------" << endl;
	monster.attacked(100);
	cout << "---------------------" << endl;
	return 0;
}

#endif 

运行结果如下图所示:

 

代码中增加了死亡状态,当怪物血量小于0时进入了死亡状态。

采用状态模式可以很方便地扩展,增加新的状态会非常便捷。

3. 单例状态对象

当状态对象不含有自身内部的状态时,可以考虑将状态类实现成单例模式

4. 状态模式与策略模式的辨析

状态模式策略模式的结构十分类似,两种模式的差别在于它们的目的不同。

  • 对于状态模式来说,可以将对象的一组行为封装在一个状态对象中,context对象(拥有状态的对象)的行为可以随时通过切换不同的状态对象而改变。随着时间的流逝,当前的状态也在多个状态对象中发生改变,以反映context内部的状态,context的行为也会改变。但是对于客户端代码对于context的状态对象可以毫无察觉,不需要了解。context的状态切换是在其内部进行的,客户端代码感知不到客户端只能感受到context的行为产生了变化,而无需了解其当前的状态是怎么样的
  • 对于策略模式来说,通常需要客户端主动指定context当前所需要的策略对象是哪一个。策略模式允许在程序运行过程中改变策略,但是对于某个context对象来说,通常只有一个当前最适合的策略。策略的切换是通过客户端代码主动发起的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958428.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GESP202309 三级【进制判断】题解(AC)

》》》点我查看「视频」详解》》》 [GESP202309 三级] 进制判断 题目描述 N N N 进制数指的是逢 N N N 进一的计数制。例如&#xff0c;人们日常生活中大多使用十进制计数&#xff0c;而计算机底层则一般使用二进制。除此之外&#xff0c;八进制和十六进制在一些场合也是常用…

汽车敏捷开发:项目经理如何精准跟进项目流程

在敏捷开发环境中&#xff0c;项目经理身兼协调者、推动者、决策者等关键角色。 作为协调者&#xff0c;需在团队及部门间搭建沟通桥梁&#xff0c;确保信息流畅。 作为推动者&#xff0c;面对迭代中的技术难题、资源短缺等阻碍&#xff0c;要主动寻找解决方案&#xff0c;为…

数据从前端传到后端入库过程分析

数据从前端传到后端入库过程分析 概述 积累了一些项目经验&#xff0c;成长为一个老程序员了&#xff0c;自认为对各种业务和技术都能得心应手的应对了&#xff0c;殊不知很多时候我们借助了搜索引擎的能力&#xff0c;当然现在大家都是通过AI来武装自己。 今天要分析的话题是…

Netty 实战

Netty实践 1 Netty 版本选择2 Netty 模版代码2.1 Server2.2 Client 3 组件3.1 EventLoop、EventLoopGroup3.1.1 EventLoop3.1.2 EventLoopGroup 3.2 Channel3.2.1 ChannelFuture3.2.2 CloseFuture 3.3 ChannelHandler3.2.1 常用的 ChannelInboundHandlerAdapter3.2.1.1 LineBas…

Triton:内存高效注意力机制的实现与解析

Triton:内存高效注意力机制的实现与解析 引言 在深度学习领域&#xff0c;特别是自然语言处理&#xff08;NLP&#xff09;任务中&#xff0c;注意力机制是模型理解序列数据的关键组成部分。然而&#xff0c;随着模型规模和输入长度的增长&#xff0c;传统的注意力机制面临着…

微信小程序使用上拉加载onReachBottom。页面拖不动。一直无法触发上拉的事件。

1&#xff0c;可能是原因是你使用了scroll-view的标签&#xff0c;用onReachBottom触发加载事件。这两个是有冲突的。没办法一起使用。如果页面的样式是滚动的是无法去触发页面的onReachBottom的函数的。因此&#xff0c;你使用overflow:auto.来使用页面的某些元素滚动&#xf…

机器学习2 (笔记)(朴素贝叶斯,集成学习,KNN和matlab运用)

朴素贝叶斯模型 贝叶斯定理&#xff1a; 常见类型 算法流程 优缺点 集成学习算法 基本原理 常见方法 KNN&#xff08;聚类模型&#xff09; 算法性质&#xff1a; 核心原理&#xff1a; 算法流程 优缺点 matlab中的运用 朴素贝叶斯模型 朴素贝叶斯模型是基于贝叶斯…

【2024年华为OD机试】(B卷,100分)- 非严格递增连续数字序列 (JavaScriptJava PythonC/C++)

一、问题描述 题目描述 给定一个仅包含大小写字母和数字的字符串&#xff0c;要求找出其中最长的非严格递增连续数字序列的长度。非严格递增连续数字序列指的是序列中的数字从左到右依次递增或保持不变&#xff0c;例如 12234 就是一个非严格递增连续数字序列。 输入描述 输…

Android中Service在新进程中的启动流程2

目录 1、Service在客户端的启动入口 2、Service启动在AMS的处理 3、Service在新进程中的启动 4、Service与AMS的关系再续 上一篇文章中我们了解了Service在新进程中启动的大致流程&#xff0c;同时认识了与客户端进程交互的接口IApplicationThread以及与AMS交互的接口IActi…

Three城市引擎地图插件Geo-3d

一、简介 基于Three开发&#xff0c;为Three 3D场景提供GIS能力和城市底座渲染能力。支持Web墨卡托、WGS84、GCJ02等坐标系&#xff0c;支持坐标转换&#xff0c;支持影像、地形、geojson建筑、道路&#xff0c;植被等渲染。支持自定义主题。 二、效果 三、代码 //插件初始化…

Ubuntu环境 nginx 源码 编译安装

ubuntu 终端 使用 wget 下载源码 sudo wget http://nginx.org/download/nginx-1.24.0.tar.gz解压刚下载的源码压缩包 nginx-1.24.0.tar.gz sudo tar -zxvf nginx-1.24.0.tar.gz 解压完成 产生 nginx-1.24.0 目录 进入该目录 cd ./nginx-1.24.0 目录下有一个可执行文件 con…

【深度学习】神经网络实战分类与回归任务

第一步 读取数据 ①导入torch import torch ②使用魔法命令&#xff0c;使它使得生成的图形直接嵌入到 Notebook 的单元格输出中&#xff0c;而不是弹出新的窗口来显示图形 %matplotlib inline③读取文件 from pathlib import Path import requestsDATA_PATHPath("dat…

60,【1】BUUCF web [RCTF2015]EasySQL1

先查看源码 1&#xff0c;changepwd&#xff08;修改密码&#xff09; <?php // 开启会话&#xff0c;以便使用会话变量 session_start();// 设置页面的内容类型为 HTML 并使用 UTF-8 编码 header("Content-Type: text/html; charsetUTF-8");// 引入配置文件&…

Chrome插件:图片缩放为头像(128*128)

前置条件&#xff1a; 安装有chrome谷歌浏览器的电脑 使用步骤&#xff1a; 1.打开chrome扩展插件 2.点击管理扩展程序 3.加载已解压的扩展程序 4.选择对应文件夹 5.成功后会出现一个扩展小程序 6.点击对应小程序 7.使用小程序 8.拖拽成功后会自动保存到下载 代码&#xf…

machine learning knn算法之使用KNN对鸢尾花数据集进行分类

通过导入必要的scikit-learn导入必要的库&#xff0c;加载给定的数据&#xff0c;划分测试集和训练集之后训练预测和评估即可 具体代码如下&#xff1a; import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split f…

电子应用设计方案102:智能家庭AI鱼缸系统设计

智能家庭 AI 鱼缸系统设计 一、引言 智能家庭 AI 鱼缸系统旨在为鱼类提供一个健康、舒适的生活环境&#xff0c;同时为用户提供便捷的管理和观赏体验。 二、系统概述 1. 系统目标 - 自动维持水质稳定&#xff0c;包括水温、酸碱度、硬度和溶氧量等关键指标。 - 智能投食&…

【C语言系列】深入理解指针(3)

深入理解指针&#xff08;3&#xff09; 一、字符指针变量二、数组指针变量2.1数组指针变量是什么&#xff1f;2.2数组指针变量怎么初始化&#xff1f; 三、二维数组传参的本质四、函数指针变量4.1函数指针变量的创建4.2函数指针变量的使用4.3两段有趣的代码4.4 typedef关键字 …

2024年度总结-CSDN

2024年CSDN年度总结 Author&#xff1a;OnceDay Date&#xff1a;2025年1月21日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 文章目录 2024年CSDN年度总结1. 整体回顾2…

Linux下php8安装phpredis扩展的方法

Linux下php8安装phpredis扩展的方法 下载redis扩展执行安装编辑php.ini文件重启php-fpmphpinfo 查看 下载redis扩展 前提是已经安装好redis服务了 php-redis下载地址 https://github.com/phpredis/phpredis 执行命令 git clone https://github.com/phpredis/phpredis.git执行…

我的2024年度历程回顾

一、自我介绍 这个是我的 个人主页 &#xff1a; zxctscl 从2023年到现在一些小成就 我主要分享的文章是C语言和C方面&#xff1a; 当然也有不少算法题&#xff1a; 二、年度回顾 在过去的一年里&#xff0c;也有不少收获&#xff1a; 在C编程语言的学习方面取得了显著…