学以致用C++设计模式 之 “状态模式”
时间:2022-07-23
本文章向大家介绍学以致用C++设计模式 之 “状态模式”,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
从DFA讲起
何为DFA?确定的有穷状态机。这里
看完上面那篇博客,就明白状态机还挺好用的。 那么现在,转场。
游戏角色
要开发一款游戏,咱负责的模块是处理游戏角色属性框架的搭建,目前已知角色有:坦克、法师、射手,他们都有属性:血量、物攻、物抗、法攻、法抗、角色转换技能。
同样,可以采用自动机的方式来进行处理:
坦克 |
法师 |
射手 |
|
---|---|---|---|
坦克 |
- |
双抗-,法攻+ |
双抗-,物攻+ |
法师 |
双抗+,法攻- |
- |
物攻+,法攻- |
射手 |
双抗+,物攻- |
物攻-,法攻+ |
- |
也挺好办的,就这样封装好了。
但是,这毕竟是C语言的方式,面向过程,如果我要拓展,加俩新角色进去呢?加个近战战士,怎么办?把写好的代码拆了重写?那明天再加个辅助进去,再重写?
既然学了面向对象,那就用面向对象的方式来解决这类问题:将每种状态封装成类,然后该怎么办就不用我多说了。
类怎么设计呢?看这样好不好:
class 坦克{
private:
int 血量;
int 物攻;
int 物抗;
int 法攻;
int 法抗;
public:
void 攻击();
void 防守();
void 技能();
}
可还行? 然后场景类就这样:
int main(){
class* 坦克兵 = new 坦克();
class* 魔法师 = new 法师();
class* 狙击手 = new 射手();
//被人打了,用坦克
坦克兵->防守();
//要打消耗,用法师
魔法师->攻击();
//敌人要跑,用射手
狙击手->技能();
···
}
这样始终是零散,疏于管教。而且,并不能实现自动啊,顶多就是个手动状态机。 那怎么弄哦?将前边的DFA再看看,再配上责任链的思想,想想就知道该来了父类,然后每个子类中自己管理自己的状态,再维护一条状态链表,实现有穷的自动状态机(C++状态模式版)。
你看这样可好?
class 状态基类{
private:
string 当前状态;
状态基类* 下一状态;
public:
virtual void 攻击();
virtual void 防守();
virtual void 技能();
void setState(string 状态){this->当前状态 = 状态;}
void setNextNode(状态基类* 下一状态){this->下一状态 = 下一状态;};
状态基类* 遍历状态(string 目标状态){
if(this->当前状态 == 目标状态)
return this;
else if(this->下一状态 != NULL)
下一状态->遍历状态(目标状态);
else
return NULL;
}
}
class 坦克:public 状态基类{
public:
void 攻击(){···}
void 防守(){···}
void 技能(){···}
}
···
场景类就这样实现了:
状态基类* 状态1 = new 坦克();
状态基类* 状态2 = new 战士();
状态基类* 状态3 = new 法师();
状态基类* 状态4 = new 射手();
状态基类* 当前状态;
状态1->setState("坦克");
状态2->setState("战士");
状态3->setState("法师");
状态4->setState("射手");
状态1->setNextNode(状态2);
状态2->setNextNode(状态3);
状态3->setNextNode(状态4);
//被人打了
当前状态 = 状态1->遍历状态(坦克);
当前状态->防守();
//要打消耗,用法师
当前状态 = 状态1->遍历状态(法师);
当前状态->攻击();
//敌人要跑,用射手
当前状态 = 状态1->遍历状态(射手);
当前状态->技能();
//中埋伏了,换战士
当前状态 = 状态1->遍历状态(战士);
当前状态->攻击();
当前状态->防守();
当前状态->放技能();
这样连轴转。
什么是状态模式
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其种类。
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
状态模式的应用
优点:
结构清晰、遵循设计原则、封装性好
缺点:
要避免状态过多。
- (Head First 设计模式)学习笔记(1)
- [c#]Webservice中如何实现方法重载(overload)以及如何传送不能序列化的对象作参数
- Web.Config文件配置小记
- [原创]web application中使用Profile应该注意的问题
- MRTG FOR WINDOWS 安装指南
- 几种常见复合sql查询语句的linq写法[继续补充中]
- [原创]在msmq3.0中使用http协议发送消息
- 微信小程序开发探索之路
- 前端周记 2017 年终总结
- asp.net mvc中的路径选择
- MVC中实现加载更多
- 在ASP.NET MVC 中获取当前URL、controller、action
- [转自Scott]ASP.NET MVC框架(第四部分): 处理表单编辑和提交场景
- MVC前台Post/Get异步获得数据时参数的取值问题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python数据分析之基础篇(三)
- Python数据分析之matplotlib(基础篇)
- Python数据分析之matplotlib(提高篇)
- Python数据分析之matplotlib(应用篇)
- Intel OpenCL 之 Pipeline(一):基本概念
- Intel OpenCL 之 Pipeline(二)For循环的执行机制
- Intel OpenCL 之 Pipeline(三)不能pipeline的可能情况
- Intel OpenCL 之 Pipeline(四):Pipeline不理想的几种情况
- Python数据分析之NumPy(基础篇)
- Python数据分析之NumPy(运算篇)
- Python数据分析之NumPy(高级篇)
- Python数据分析之Pandas(数据结构)
- Python数据分析之Pandas(数据操作)
- Python数据分析之Seaborn(样式风格)
- Python数据分析之Seaborn(配色方案)