设计模式:状态机模式

2023-11-08

首先状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;

这里说一个在RTS游戏里的应用:有限状态机。

我们要赋予每个战斗单位一个智能,比如一定范围内检测到地方单位,且自身处于游荡或者Patrol状态,那么就转换为攻击状态,再比如我们给这个单位一个通往目的地的路径,那么这个单位会自动前往目的地,如果到达目的地,那么就进入Idle状态。

这里我们可以写一个状态管理类:

class StateSystem{
    void State state;
public:
    void Run(){
        if(state==State::Idle){
            //...       
            if(传递路径){
                LeaveState();
                state==State::Path;
                 InitState();
            }
        }else if(state==State::Path){
            //...
            if(//到达终点){
                 LeaveState();
                 state=State::Idle;
                 InitState();
            }
        }else if(state==State::Patrol){
            //...
            if(周边有敌人){
                 LeaveState();
                state=State::Attack;
                 InitState();
            }
        }else if(state==State::Attack){
            //...
            if(敌人被消灭){
                LeaveState();
                state=Satte::Idle;
                 InitState();
            }
        }
    }
    
    void InitState(){
        if(state==State::Idle){
            //...       
        }else if(state==State::Path){
            //...
        }else if(state==State::Patrol){
            //...
        }else if(state==State::Attack){
            //...
        }
    }

    void LeaveState(){
        if(state==State::Idle){
            //...       
        }else if(state==State::Path){
            //...
        }else if(state==State::Patrol){
            //...
        }else if(state==State::Attack){
            //...
        }
    }
}

可以看到,典型的bad smell,这里我们可以将每个状态抽象出来,设计好接口,然后用多态来替代if_else。

这个就是状态机模式的精髓,因为处理的问题就是一个类的方法处理信息的模式和类的状态有关,那么换句话说技术类中的每个状态都要处理相同的信息,那么我们为什么不把这种状态抽离出来呢,然后单独考虑呢?这样我们可以在原本的类中存储这些类的抽象接口,然后通过接口来调用这些状态。

看以下代码:

#include<unordered_map>

enum State {
	Idle,Patrol,Path,Attack,None
};

class State_Ifc {
	State state;
public:
	virtual void Init() = 0;//初始化状态
	virtual void Leave() = 0;//卸载状态
	virtual void Run() = 0;//处理信息
	virtual State Reason() = 0;//转移到其他状态
	virtual State GetState() { return state; }
};

class IdleState :public State_Ifc {
	//....重写
};

class PatrolState :public State_Ifc {
	//....重写
};

class PathState :public State_Ifc {
	//....重写
};

class AttackState :public State_Ifc {
	//....重写
};

class StateSystem {
	State_Ifc* Current;//当前的状态
	std::unordered_map<State, State_Ifc*> map;
	//
public:
	void UpData() {
		Current->Run();
	}
	void Trans() {
		State temp = Current->Reason();
		if (temp != State::None) {
			Current->Leave();
			Current = map[temp];
			Current->Init();
		}
	}
};

这样子的话,State不依赖于具体的状态,而是依赖于抽象接口,这样子的话,就为拓展带来了可能。

但是,这个模式也有坏处,那就是没当添加新的状态,如果这个状态有和其他状态有转换,那么我们就不得不修改源代码,这里,本菜想,可以通过中介者模式来解决这个问题,具体方法是将
Reason方法从State子类中抽离出来,这样,我们修改转换时,只需要修改Mediator类即可:

具体如下:

#include<unordered_map>
#include<list>
enum State {
	Idle,Patrol,Path,Attack,None
};

class Mediator_Ifc {
public:
	virtual State Trans(State stateS, State stateE) = 0;
};

class State_Ifc {
	std::list<State> list;
	State state;
	Mediator_Ifc* Mediator;
public:
	virtual void Init() = 0;//初始化状态
	virtual void Leave() = 0;//卸载状态
	virtual void Run() = 0;//处理信息
	virtual State Reason() {
		for (auto t : list) {
			State temp = Mediator->Trans(state, t);
			if (temp != State::None) {
				return temp;
			}
		}
		return State::None;
	}
	virtual State GetState() { return state; }
};

class IdleState :public State_Ifc {
	//....重写
};

class PatrolState :public State_Ifc {
	//....重写
};

class PathState :public State_Ifc {
	//....重写
};

class AttackState :public State_Ifc {
	//....重写
};


class MediatorCenter :public Mediator_Ifc {
	State Trans(State stateS, State stateE) {
		//...实现
	}
};

class StateSystem {
	Mediator_Ifc* Mediator;
	State_Ifc* Current;//当前的状态
	std::unordered_map<State, State_Ifc*> map;
	//
public:
	void UpData() {
		Current->Run();
	}
	void Trans() {
		State temp = Current->Reason();
		if (temp != State::None) {
			Current->Leave();
			Current = map[temp];
			Current->Init();
		}
	}
};

这样就可以了。

总之一句话:状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式:状态机模式 的相关文章

  • 23种设计模式之装饰模式

    装饰模式 一个简陋的房子 它可以让人在里面居住 为人遮风避雨 但如果给它进行装修 那么它的居住环境就更加宜人了 程序中的对象也与房子十分类似 首先有一个相当于 房子 的对象 然后经过不断装饰 不断对其增加功能 它就变成了使用功能更加强大的对
  • 设计模式-单一职责原则介绍与理解

    描述 一个类应该专注于实现一个功能 好处 便于代码复用 举例 俄罗斯方块游戏 首先可以想到的是游戏逻辑与界面的分离 也就是说逻辑一个类 界面部分一个类 这样做的好处就是我们可以复用游戏逻辑的代码 例如我们用java写了一个基于PC端的俄罗斯
  • 行为型模式-策略模式

    package per mjn pattern strategy 抽象策略类 public interface Strategy void show package per mjn pattern strategy 具体策略类 用来封装算法
  • 设计模式之(三)---工厂方法模式

    女娲补天的故事大家都听过吧 这个故事是说 女娲在补了天后 下到凡间一看 哇塞 风景太优美了 天空是湛 蓝的 水是清澈的 空气是清新的 太美丽了 然后就待时间长了就有点寂寞了 没有动物 这些看的到 都是静态的东西呀 怎么办 别忘了是神仙呀 没
  • 设计模式——原型模式

    原型模式顾名思义 就是指以某个实例为原型 copy出一个新的实例 该实例属性与原型相同或者是类似 很多时候 我们需要创建大量的相同或者相似的对象 如果一个个用new 构造函数的形式去创建的话比较繁琐 就像孙悟空要想变出成千上万个猴子猴孙总不
  • java需会(转载)

    一 基础篇 1 1 Java基础 面向对象的特征 继承 封装和多态 final finally finalize 的区别 Exception Error 运行时异常与一般异常有何异同 请写出5种常见到的runtime exception i
  • 程杰“大话设计模式”中的设计原则

    单一职责原则 SRP 就一个类而言 应该仅有一个引起它变化的原因 如果一个类承担的职责过多 就等于把这些职责耦合在了一起 一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力 这种耦合会导致脆弱的设计 当发生变化时 设计会遭受到意想不到
  • 设计模式(十)装饰器模式

    装饰器模式是一种非常有用的结构型模式 它允许我们在不改变类的结果的情况下 为类添加新的功能 我们来举例说明一下 首先添加一组形状 它们都实现了形状接口 public interface Shape String getShape class
  • linux内核中的设计模式

    创建型 Object Pool Object Pool模式可以提升性能 尤其是在对象的分配 初始化成本高 使用频率高 但使用时间短的情况下 对象池可以设置对象池的大小和回收时间缓存预分配的对象 NT和Linux都有简单的预分配缓存对象的机制
  • 设计模式学习笔记-工厂模式

    设计模式学习笔记 工厂模式 作用 实现了创建者和调用者的分离 详细分类 简单工厂模式 用来生产同一等级结构中的任意产品 对于增加新的产品 必须要扩展已有的代码 工厂方法模式 用来生产同一等级结构中的固定产品 支持增加任意产品 抽象工厂模式
  • 单例模式的八种写法比较

    单例模式是最常用到的设计模式之一 熟悉设计模式的朋友对单例模式都不会陌生 一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式 但是除了这两种方式 本文还会介绍其他几种实现单例的方式 让我们来一起看看吧 简介 单例模式是一种常
  • 哈工大2020软件构造Lab3实验报告

    本项目于4 21日实验课验收 更新完成 如果有所参考 请点点关注 点点赞GitHub Follow一下谢谢 2020春计算机学院 软件构造 课程Lab3实验报告 Software Construction 2020 Spring Lab 3
  • 泛型与反射机制在JDBC和Servlet编程中的实践

    写在前面 泛型与反射是java中的两种强大机制 可以很好的提高代码的灵活性和复用性 本篇文章向大家展现在JDBC和Servlet编程场景下反射和泛型技术的实践 通过灵活使用这两种机制打造 高度可复用的JDBC和Servlet代码 1 JDB
  • 设计模式详解---策略模式

    1 策略模式简介 策略模式 Strategy Pattern 是一种行为型设计模式 用于在运行时根据不同的情境选择不同的算法或策略 该模式将算法封装成独立的类 使得它们可以相互替换 而且可以独立于客户端使用它们的方式 1 1 主要角色 上下
  • 设计模式—迭代器模式解析

    本文参考学习了 图解设计模式 中的代码实现和原理解释 迭代器模式 简介 Iterator 模式用于在数据集合中按照顺序遍历集合 就类似于我们的循环 一个个去遍历一个集合中的所有元素 示例代码 首先我们思考一个书本和书架的关系 显然 书架可以
  • 在AI技术的无情侵袭下,学学Java的23种设计模式还是非常有必要的

    目前国内80 程序员的主要工作是调用组合api实现各种业务需求 在顶层架构师设定好的框架下 做着重复且无聊的编码工作 如果未来ai被广泛应用 那么被替代的风险是很高的 比较扎心的是 其实目前用ai生成片段代码已经是各个公司比较普遍的做法了
  • Java设计模式:模板方法模式

    作者主页 欢迎来到我的技术博客 个人介绍 大家好 本人热衷于 Java后端开发 欢迎来交流学习哦 如果文章对您有帮助 记得 关注 点赞 收藏 评论 您的支持将是我创作的动力 让我们一起加油进步吧 文章目录 一 模板方法模式的定义 二 模板方
  • 设计模式 原型模式 与 Spring 原型模式源码解析(包含Bean的创建过程)

    原创 疯狂的狮子Li 狮子领域 程序圈 2023 12 19 10 30 发表于辽宁 原型模式 原型模式 Prototype模式 是指 用原型实例指定创建对象的种类 并且通过拷贝这些原型 创建新的对象 原型模式是一种创建型设计模式 允许一个
  • 自动化测试面试题(附答案)

    1 自动化代码中 用到了哪些设计模式 单例设计模式 工厂模式 PO设计模式 数据驱动模式 面向接口编程设计模式 2 什么是断言 Assert 断言Assert用于在代码中验证实际结果是不是符合预期结果 如果测试用例执行失败会抛出异常并提供断
  • 系列一、 单例设计模式

    一 单例设计模式 1 1 概述 单例模式 Singleton Pattern 是Java中最简单的设计模式之一 这种类型的设计模式属于创建者模式 它提供了一种创建对象的最佳方式 这种模式涉及到一个单一的类 该类负责创建自己的对象 同时确保只

随机推荐

  • Would you like Visual Studio Code to periodically run “git fetch“?是什么意思?

    这句话的意思是 你是否想让Visual Studio Code定期运行 git fetch 命令 git fetch 是 Git 命令之一 用于从远程代码仓库获取最新的代码变动 但不会自动将这些变动合并到你当前所在的分支 该命令通常用于更新
  • Python os.path模块的使用

    Python os path模块的使用 Python的os模块是一个对接操作系统的模块 当我们需要对路径进行操作时 可以使用os path os path模块实现了很多处理长文件名 长路径名的函数 可以用来对路径切分 拼接 转换等 先导入o
  • Seata分布式事务失效踩坑记录

    在学习Seata过程中 偶然发现了一个坑 这里做个记录 环境说明 先说下我的环境 我是搭建了一个SpringCloud微服务 然后A服务调用B服务 然后在这两个服务都集成了Seata 集成过程是没有问题的 业务场景是注册场景 A服务中需要操
  • 01-java学习笔记【接口与抽象类】

    这些是我自己的理解加上网上优秀的分享总结出来的 抽象类是用来捕捉子类的通用特性的 它不能被实例化 只能被用作子类的超类 抽象类是被用来创建继承层级里子类的模板 接口是抽象方法的集合 如果一个类实现了某个接口 那么它就继承了这个接口的抽象方法
  • papers with code介绍(人工智能方向研究生的必备网站)

    paperswithcode介绍 人工智能方向的必备网站 本文将从两个部分介绍 一 正文 二 导航 A browse State of the Art B Datasets C Method D More 网站首页 一 正文 2 最上面是四
  • Python中RSA加密

    文章目录 RSA加密 一 概述 1 简介 2 签名 3 环境配置 二 算法实现 1 公钥和私钥 2 加密和解密 3 签名和解签 RSA加密 一 概述 1 简介 RSA是非对称的 也就是用来加密的密钥和用来解密的密钥不是同一个 和DES一样的
  • LCD背光控制芯片

    PWM信号可通过调整占空比来调节输出电压 可以使用PWM来控制LCD的背光 但CPU的pwm引脚驱动能力太弱 常外接一个背光芯片 rt9293就是这样的一个恒流升压转换器 Iled Vref Rset Vfb Duty 300mV Duty
  • 论文阅读_大语言模型_Llama2

    英文名称 Llama 2 Open Foundation and Fine Tuned Chat Models 中文名称 Llama 2 开源的基础模型和微调的聊天模型 文章 http arxiv org abs 2307 09288 代码
  • js怎样判断引用类型和值类型?

    1 typeof 输出的类型 console log typeof y undefined console log typeof 101 number console log typeof hello string console log
  • 如何使用logging生成日志

    GiantPandaCV导语 日志对程序执行情况的排查非常重要 通过日志文件 可以快速定位出现的问题 本文将简单介绍使用logging生成日志的方法 logging模块介绍 logging是python自带的包 一共有五个level deb
  • https 访问 iframe 的http

    最近做的项目要求https 嵌入http的项目 浏览器老是提示https不能访问http 为了满足需求 在本地项目中添加了nginx转发服务 将项目中的iframe 转发为https 再又nginx将https转发至http 满足需求了 n
  • 1. 经验累积分布函数

    为了说明经验累积分布函数 我们这里使用一个学生成绩的数据集 假设班有50名学生 这些学生刚刚进行了一个测试 这个测试的结果是以0 100的分数来体现的 我们要如何更好的可视化结果呢 例如确定成绩的最大值和最小值 对于这个数据的可视化 我们可
  • jar包修改并重新打包,jar包反编译使用工具以及修改代码方法

    jar包修改并重新打包 jar包反编译使用工具以及修改代码方法 备忘 https blog csdn net tomcat zhu article details 79240011
  • unity中的reflectionProbe的使用

    下面说一下Reflection Probe 大家都知道 当使用标准着色器时 每一个材质都会具有一定程度的镜面反射 specularity 和金属反射 metalness 属性 在没有强大的硬件来处理即时光迹追踪反射的情况下 我们得仰赖预先计
  • 8.bidirectional_recurrent_neural_network

    import torch import torch nn as nn import torchvision import torchvision transforms as transforms device torch device cu
  • 电源升降压芯片电路归纳(归纳中。。。)

    注 学习硬件做的总结 大神勿喷 有不足之处还望不吝赐教 目录 电源升压5v芯片归纳 PS7516电路 NCP1400ASN50电路 PL2628电路 FP6276B电路 PL2303电路 PS3120A电路 QX2301LXXE电路 电源升
  • Opencv快速入门教程,Python计算机视觉基础

    快速入门 OpenCV 是 Intel 开源计算机视觉库 它由一系列 C 函数和少量 C 类构成 实现了图像处理和计算机视觉方面的很多通用算法 OpenCV 拥有包括 300 多个 C 函数的跨平台的中 高层 API 它不依赖于其它的外部库
  • 用java解一元二次方程组

    System out print 求 ax 2 bx c 0的根 n Scanner in new Scanner System in 定义变量 while true System out println 请输入a的值 int a in n
  • RT-Thread开发GD32F450 添加adc外设

    开发板使用的是gd32f450zk env工具使用的版本是1 3 5 rtthread版本是5 0 0 添加adc外设的步骤如下 步骤1 查看开发板的电路原理图 确定adc的使用引脚 使用的是引脚PF6 即adc012 IN4 如下图 步骤
  • 设计模式:状态机模式

    首先状态机模式是处理一个类在内部状态改变的时候 其方法处理信息的模式也会改变 这里说一个在RTS游戏里的应用 有限状态机 我们要赋予每个战斗单位一个智能 比如一定范围内检测到地方单位 且自身处于游荡或者Patrol状态 那么就转换为攻击状态