[设计模式][c++]状态切换模式
转自:http://blog.csdn.net/yongh701/article/details/49154439
状态模式也是设计模式的一种,这种设计模式思想不复杂,就是实现起来的代码有点复杂。主要出现在类传递参数上,尤其是C++这种不能直接类间互相调用都语言,实现状态模式更难,当然,一切设计模式都是为了更简短的主函数。
状态模式是当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类,主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。主要有以下三种角色:
1、上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
2、抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。
3、具体状态(Concrete State):实现抽象状态定义的接口。
说是这样的意思:
举个例子来说明吧,如下图:
现在要求再主函数中,直接一行代码context->switch_state();从状态A切到状态B。
这里利用状态模式来实现,具体状态就是状态A与状态B,然后上下文存在一个“转换状态”的方法。之后状态A与状态B共同接口就是抽象状态State。具体实现代码如下:
- #include<iostream>
- using namespace std;
- class Context;//类,上下文,的提前引用,主要是用于通过C++的编译,需要提前声明这个类
- class State{//抽象状态
- public:
- virtual void switch_state()=0;//其实就是一个接口,转换状态的方法switch_state()全部写在这里
- };
- //具体状态
- //每一个具体状态,都必须有私有变量上下文Context *context;
- //每一个具体状态的构造方法,都必须用this->context=context;实现将自己注册到上下文中。
- //不得在每一个具体状态中实现转换状态的方法switch_state(),只能在类外实现,因为C++禁止类的互相调用,否则会出现error C2027: 使用了未定义类型的错误
- class StateA:public State{
- private:
- Context *context;
- public:
- StateA(Context *context){
- this->context=context;
- }
- void switch_state();
- };
- class StateB:public State{
- private:
- Context *context;
- public:
- StateB(Context *context){
- this->context=context;
- }
- void switch_state();
- };
- //上下文的实现,里面包含一个设置抽象状态的方法,各个取具体状态的方法。
- /*
- 同时,抽象状态中定义的实现状态方法,这里要有
- void switch_state(){
- state->switch_state();
- }
- 的实现,用于暴露给客户端调用
- */
- class Context{
- private:
- State *stateA,*stateB,*state;
- public:
- Context(){
- stateA=new StateA(this);
- stateB=new StateB(this);
- this->state=stateA;
- }
- void switch_state(){
- state->switch_state();
- }
- void setState(State* state){
- this->state=state;
- }
- State* getStateA(){
- return stateA;
- }
- State* getStateB(){
- return stateB;
- }
- };
- //各个具体状态中,所对应转换状态方法。
- void StateA::switch_state(){
- this->context->setState(this->context->getStateB());//转换到B状态的特定写法
- cout<<"已转换到状态B"<<endl;
- };
- void StateB::switch_state(){
- this->context->setState(this->context->getStateA());//转换到A状态的特定写法
- cout<<"已转换到状态A"<<endl;
- };
- //主函数
- int main(){
- Context *context=new Context();
- context->switch_state();
- context->switch_state();
- context->switch_state();
- context->switch_state();
- return 0;
- }
运行结果如下图:
可以看到,在主函数中,只是初始化了上下文,然而不停调用上下文的switch_state()方法,却在两个具体状态A与B之间跳转。然而,同样的一句switch_state()有着不同实现,打印的内容是不同。
上述代码还有C++的特色,各个具体状态中,所对应转换状态方法,只能在类外实现,而不能在直接在StateA与StateB里面实现。因为C++不像Java,Java编译的时候一次性把所有东西读进去。C++是见一行读一行。这里Context类用到State,StateA与StateB用到了Context,类间相互调用在C++中是不行的。
同时,注意在上下文类Context的构造类,对各个具体状态初始化,也就是注册各个具体状态到上下文,否则编译是过了,却在程序中出现空指针。
那么这种状态模式到底有什么呢?这里用一道2011年下半年的软件设计师软考题目再来说明:
题目是这样的:
某大型商场内安装了多个简易的纸巾售卖机,自动出售2元钱一包的纸巾,且每次仅售出一包纸巾,纸巾售卖机的状态图如图5-1所示:
采用状态(State)模式来实现该纸巾售卖机,得到如图5-2所示的类图,其中类State为抽象类,定义了投币、退币、出纸巾等方法接口。类SoldOutState、NoQuarterState、HasQuarterState、SoldState分别对应图5-1纸巾售卖机的4种状态。售出纸巾、纸巾售卖、买有投币、有2元钱。
这里很显然,如果不用状态模式,会产生大量的if...else语句,代码将很不容易改变,,难以拓展。状态转换隐藏在条件语句中,所以并不明显未来加入的代码可能导致bug。
那么用状态模式,先来分析一下,这里具体状态有4个,分别对应4个类,TissueMachine类就是开放给主函数的上下文,而用户能够操作的地方,有3个,一个是投币、一个是退币,另一个是按“出纸巾”,这是上下文TissueMachine能给主函数调用的方法就这三个。而没有提到的售出方法dispense,是上下文自身内部的状态装换,因此只在售出纸巾这个状态中实现这个方法。不过,由于抽象状态State定义了这4个方法的接口,因此,4个具体状态都要有这4个方法,当然具体实现因状态不同而不同,具体代码如下:
- #include<iostream>
- using namespace std;
- //以下为类的定义部分
- class TissueMachine;//类的提前引用
- //抽象状态
- class State{
- public:
- virtual void insertQuarter()=0;//“投币”按钮被按下
- virtual void ejectQuarter()=0;//“退币”按钮被按下
- virtual void turnCrank()=0;//“出纸巾”按钮被按下
- virtual void dispense()=0;//正在卖出纸巾
- };
- //具体状态
- class SoldOutState:public State{//纸巾售完状态
- private:
- TissueMachine* tissueMachine;
- public:
- SoldOutState(TissueMachine *tissueMachine){
- this->tissueMachine=tissueMachine;
- }
- void insertQuarter();
- void ejectQuarter();
- void turnCrank();
- void dispense();
- };
- class NoQuarterState:public State{//没有投币状态
- private:
- TissueMachine* tissueMachine;
- public:
- NoQuarterState(TissueMachine *tissueMachine){
- this->tissueMachine=tissueMachine;
- }
- void insertQuarter();
- void ejectQuarter();
- void turnCrank();
- void dispense();
- };
- class HasQuarterState:public State{//有2元钱(已投币状态)
- private:
- TissueMachine* tissueMachine;
- public:
- HasQuarterState(TissueMachine *tissueMachine){
- this->tissueMachine=tissueMachine;
- }
- void insertQuarter();
- void ejectQuarter();
- void turnCrank();
- void dispense();
- };
- class SoldState:public State{//出售纸巾状态
- private:
- TissueMachine* tissueMachine;
- public:
- SoldState(TissueMachine *tissueMachine){
- this->tissueMachine=tissueMachine;
- }
- void insertQuarter();
- void ejectQuarter();
- void turnCrank();
- void dispense();
- };
- //上下文
- class TissueMachine{
- private:
- State *soldOutState,*noQuarterState,*hasQuarterState,*soldState,*state;
- int count;//纸巾数
- public:
- TissueMachine(int numbers){//构造函数,定义初始状态有纸巾售卖机有多少纸巾
- soldOutState=new SoldOutState(this);
- noQuarterState=new NoQuarterState(this);
- hasQuarterState=new HasQuarterState(this);
- soldState=new SoldState(this);
- this->count=numbers;
- if (count> 0) {
- this->state=noQuarterState;//开始为没有投币的状态
- }
- };
- //开放给主函数调用的方法
- void insertQuarter(){
- state->insertQuarter();
- }
- void ejectQuarter(){
- state->ejectQuarter();
- }
- void turnCrank(){
- state->turnCrank();
- state->dispense();
- }
- //数据传递的getter与setter
- void setState(State* state){
- this->state=state;
- }
- State* getHasQuarterState(){
- return hasQuarterState;
- }
- State* getNoQuarterState(){
- return noQuarterState;
- }
- State* getSoldState(){
- return soldState;
- }
- State* getSoldOutState(){
- return soldOutState;
- }
- int getCount(){
- return count;
- };
- void setCount(int numbers){
- this->count=numbers;
- };
- };
- //具体状态中各个方法的具体实现。
- //纸巾售完状态
- void SoldOutState::insertQuarter(){
- cout<<"机器无纸巾,已退回硬币!"<<endl;
- }
- void SoldOutState::ejectQuarter(){
- cout<<"自动售货机根本没有硬币!"<<endl;
- }
- void SoldOutState::turnCrank(){
- cout<<"机器无纸巾,请不要操作机器"<<endl;
- }
- void SoldOutState::dispense(){
- }
- //没有投币状态
- void NoQuarterState::insertQuarter(){
- tissueMachine->setState(tissueMachine->getHasQuarterState());
- cout<<"已投币!"<<endl;
- }
- void NoQuarterState::ejectQuarter(){
- cout<<"自动售货机根本没有硬币!"<<endl;
- }
- void NoQuarterState::turnCrank(){
- cout<<"请投币"<<endl;
- }
- void NoQuarterState::dispense(){
- }
- //有2元钱(已投币状态)
- void HasQuarterState::insertQuarter(){
- cout<<"已投币!请不要重复投币!已退回重复投币!"<<endl;
- }
- void HasQuarterState::ejectQuarter(){
- tissueMachine->setState(tissueMachine->getNoQuarterState());
- cout<<"已取币!"<<endl;
- }
- void HasQuarterState::turnCrank(){
- tissueMachine->setState(tissueMachine->getSoldState());
- cout<<"请等待自动售货机出纸巾!"<<endl;
- }
- void HasQuarterState::dispense(){
- }
- //出售纸巾状态
- void SoldState::insertQuarter(){
- cout<<"请等待自动售货机出纸巾!请不要投币!已退回投币!"<<endl;
- }
- void SoldState::ejectQuarter(){
- tissueMachine->setState(tissueMachine->getNoQuarterState());
- cout<<"请等待自动售货机出纸巾!无法取回已消费的硬币!"<<endl;
- }
- void SoldState::turnCrank(){
- cout<<"请等待自动售货机出纸巾!已响应你的操作!"<<endl;
- }
- void SoldState::dispense(){//售出纸巾动作
- if(tissueMachine->getCount()>0){
- tissueMachine->setState(tissueMachine->getNoQuarterState());
- tissueMachine->setCount(tissueMachine->getCount()-1);
- cout<<"你的纸巾,请拿好!"<<endl;
- }
- else{
- tissueMachine->setState(tissueMachine->getSoldOutState());
- cout<<"已退回你的硬币!纸巾已卖光,等待进货!"<<endl;
- }
- }
- //主函数
- int main(){
- TissueMachine *tissueMachine=new TissueMachine(1);
- cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;
- tissueMachine->insertQuarter();//投币
- tissueMachine->turnCrank();//取纸巾
- cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;//不投币取纸巾测试
- tissueMachine->turnCrank();
- cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;//售完纸巾,投币取纸巾测试
- tissueMachine->insertQuarter();
- tissueMachine->turnCrank();
- return 0;
- }
运行结果如下:
这里设置纸巾机一开始仅有1个纸巾,分别做不同的测试,可见纸巾自动售货机有不同的响应。
[设计模式][c++]状态切换模式的更多相关文章
- Head First 设计模式 —— 12. 状态 (State) 模式
思考题 public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; fi ...
- 【GOF23设计模式】状态模式
来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_状态模式.UML状态图.酒店系统房间状态.线程对象状态切换 package com.test.state; public ...
- 【转】设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)
命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...
- 设计模式 ( 十七) 状态模式State(对象行为型)
设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...
- 折腾Java设计模式之状态模式
原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...
- 设计模式:状态(State)模式
设计模式:状态(State)模式 一.前言 状态模式在某些场合中使用是非常方便的,什么叫做状态,如果大家学过<编译原理>就会明白DFA M和NFA M,在确定有限状态机和非确定有限 ...
- 北风设计模式课程---状态模式State(对象行为型)
北风设计模式课程---状态模式State(对象行为型) 一.总结 一句话总结: 状态模式 具体状态的行为在具体的状态类中就解决,不用交给外部做判断.实质是将多条件判断弄成了多个类,在不同的类中做判断 ...
- 设计模式2——状态模式State
参考链接: 设计模式之状态模式:https://www.cnblogs.com/haoerlv/p/7777789.html 设计模式系列之状态模式:https://www.jianshu.com/p ...
随机推荐
- 在Keras模型中one-hot编码,Embedding层,使用预训练的词向量/处理图片
最近看了吴恩达老师的深度学习课程,又看了python深度学习这本书,对深度学习有了大概的了解,但是在实战的时候, 还是会有一些细枝末节没有完全弄懂,这篇文章就用来总结一下用keras实现深度学习算法的 ...
- 来自阿里妈妈的iconfont(转)
转自http://www.augsky.com/775.html 随便说说两者的优缺点 其实主要是说iconfont的优点和Font Awesome的缺点.-_-|||iconfont的图标库相当巨大 ...
- python 爬取html页面
#coding=utf-8 import urllib.request def gethtml(url): page=urllib.request.urlopen(url) html=page.rea ...
- n的相反数
实例十:n的相反数 方法:result=(~n)+1 正数 负数 原数 0000 0011 1111 1111补码 1111 1100 0000 0010加一 1111 1011 0000 001 ...
- JSON自动生成相关类
开源项目地址:https://jsonclassgenerator.codeplex.com/SourceControl/latest 太好用了,这个
- Linux 远程连接sftp与ftp
linux sftp远程连接命令 sftp -oPort=60001 root@192.168.0.254 使用-o选项来指定端口号. -oPort=远程端口号 sftp> get /var/w ...
- Linux基础命令---diff
diff 逐行比较两个文本文件,把文件的差异显示到标准输出.如果要指定要比较目录,那么diff命令会比较目录中相同文件名的文件,不会比较子目录. 此命令的适用范围:RedHat.RHEL.Ubuntu ...
- JavaScript中hoisting(悬置/置顶解析/预解析) 实例解释,全局对象,隐含的全局概念
JavaScript中hoisting(悬置/置顶解析/预解析) 实例解释,全局对象,隐含的全局概念 <html> <body> <script type="t ...
- Linux中Postfix邮件发送配置(三)
部署DNS服务器 postfix根据域名和地址做一个MX记录,A记录,PTR记录(一般在互联网上邮件服务器都要反解,没有PTR记录会认为是垃圾邮件) $ service iptables stop $ ...
- python之路----继承的抽象类和接口类
抽象类与接口类 接口类 继承有两种用途: 一:继承基类的方法,并且做出自己的改变或者扩展(代码重用) 二:声明某个子类兼容于某基类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数 ...