当前位置: 首页 > news >正文

[设计模式][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。具体实现代码如下:

 

[cpp]  view plain  copy
 
 print?
  1. #include<iostream>  
  2. using namespace std;  
  3. class Context;//类,上下文,的提前引用,主要是用于通过C++的编译,需要提前声明这个类  
  4. class State{//抽象状态  
  5. public:  
  6.     virtual void switch_state()=0;//其实就是一个接口,转换状态的方法switch_state()全部写在这里  
  7. };  
  8. //具体状态  
  9. //每一个具体状态,都必须有私有变量上下文Context *context;  
  10. //每一个具体状态的构造方法,都必须用this->context=context;实现将自己注册到上下文中。  
  11. //不得在每一个具体状态中实现转换状态的方法switch_state(),只能在类外实现,因为C++禁止类的互相调用,否则会出现error C2027: 使用了未定义类型的错误  
  12. class StateA:public State{  
  13. private:  
  14.     Context *context;  
  15. public:  
  16.     StateA(Context *context){  
  17.         this->context=context;  
  18.     }  
  19.     void switch_state();  
  20. };  
  21. class StateB:public State{  
  22. private:  
  23.     Context *context;  
  24. public:  
  25.     StateB(Context *context){  
  26.         this->context=context;  
  27.     }  
  28.     void switch_state();  
  29. };  
  30. //上下文的实现,里面包含一个设置抽象状态的方法,各个取具体状态的方法。  
  31. /* 
  32.     同时,抽象状态中定义的实现状态方法,这里要有 
  33.     void switch_state(){ 
  34.         state->switch_state(); 
  35.     } 
  36.     的实现,用于暴露给客户端调用 
  37. */  
  38. class Context{  
  39. private:  
  40.     State *stateA,*stateB,*state;  
  41. public:  
  42.     Context(){  
  43.         stateA=new StateA(this);  
  44.         stateB=new StateB(this);  
  45.         this->state=stateA;  
  46.     }  
  47.     void switch_state(){  
  48.         state->switch_state();  
  49.     }  
  50.     void setState(State* state){  
  51.         this->state=state;  
  52.     }  
  53.     State* getStateA(){  
  54.         return stateA;  
  55.     }  
  56.     State* getStateB(){  
  57.         return stateB;  
  58.     }  
  59. };  
  60. //各个具体状态中,所对应转换状态方法。  
  61. void StateA::switch_state(){  
  62.     this->context->setState(this->context->getStateB());//转换到B状态的特定写法  
  63.     cout<<"已转换到状态B"<<endl;  
  64. };  
  65. void StateB::switch_state(){  
  66.     this->context->setState(this->context->getStateA());//转换到A状态的特定写法  
  67.     cout<<"已转换到状态A"<<endl;  
  68. };  
  69. //主函数  
  70. int main(){  
  71.     Context *context=new Context();  
  72.     context->switch_state();  
  73.     context->switch_state();  
  74.     context->switch_state();  
  75.     context->switch_state();  
  76.     return 0;  
  77. }  


运行结果如下图:

 

可以看到,在主函数中,只是初始化了上下文,然而不停调用上下文的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个方法,当然具体实现因状态不同而不同,具体代码如下:

 

[cpp]  view plain  copy
 
 print?
  1. #include<iostream>  
  2. using namespace std;  
  3. //以下为类的定义部分  
  4. class TissueMachine;//类的提前引用  
  5. //抽象状态  
  6. class State{  
  7. public:  
  8.     virtual void insertQuarter()=0;//“投币”按钮被按下  
  9.     virtual void ejectQuarter()=0;//“退币”按钮被按下  
  10.     virtual void turnCrank()=0;//“出纸巾”按钮被按下  
  11.     virtual void dispense()=0;//正在卖出纸巾  
  12. };  
  13.   
  14. //具体状态  
  15. class SoldOutState:public State{//纸巾售完状态  
  16. private:  
  17.     TissueMachine* tissueMachine;  
  18. public:  
  19.     SoldOutState(TissueMachine *tissueMachine){  
  20.         this->tissueMachine=tissueMachine;  
  21.     }  
  22.     void insertQuarter();  
  23.     void ejectQuarter();  
  24.     void turnCrank();  
  25.     void dispense();  
  26. };  
  27. class NoQuarterState:public State{//没有投币状态  
  28. private:  
  29.     TissueMachine* tissueMachine;  
  30. public:  
  31.     NoQuarterState(TissueMachine *tissueMachine){  
  32.         this->tissueMachine=tissueMachine;  
  33.     }  
  34.     void insertQuarter();  
  35.     void ejectQuarter();  
  36.     void turnCrank();  
  37.     void dispense();  
  38. };  
  39. class HasQuarterState:public State{//有2元钱(已投币状态)  
  40. private:  
  41.     TissueMachine* tissueMachine;  
  42. public:  
  43.     HasQuarterState(TissueMachine *tissueMachine){  
  44.         this->tissueMachine=tissueMachine;  
  45.     }  
  46.     void insertQuarter();  
  47.     void ejectQuarter();  
  48.     void turnCrank();  
  49.     void dispense();  
  50. };  
  51. class SoldState:public State{//出售纸巾状态  
  52. private:  
  53.     TissueMachine* tissueMachine;  
  54. public:  
  55.     SoldState(TissueMachine *tissueMachine){  
  56.         this->tissueMachine=tissueMachine;  
  57.     }  
  58.     void insertQuarter();  
  59.     void ejectQuarter();  
  60.     void turnCrank();  
  61.     void dispense();  
  62. };  
  63.   
  64. //上下文  
  65. class TissueMachine{  
  66. private:  
  67.     State *soldOutState,*noQuarterState,*hasQuarterState,*soldState,*state;  
  68.     int count;//纸巾数  
  69. public:  
  70.     TissueMachine(int numbers){//构造函数,定义初始状态有纸巾售卖机有多少纸巾  
  71.         soldOutState=new SoldOutState(this);  
  72.         noQuarterState=new NoQuarterState(this);  
  73.         hasQuarterState=new HasQuarterState(this);  
  74.         soldState=new SoldState(this);  
  75.         this->count=numbers;  
  76.         if (count> 0) {    
  77.             this->state=noQuarterState;//开始为没有投币的状态  
  78.         }  
  79.     };  
  80.     //开放给主函数调用的方法  
  81.     void insertQuarter(){  
  82.         state->insertQuarter();  
  83.     }  
  84.     void ejectQuarter(){  
  85.         state->ejectQuarter();  
  86.     }  
  87.     void turnCrank(){  
  88.         state->turnCrank();  
  89.         state->dispense();  
  90.     }  
  91.     //数据传递的getter与setter  
  92.     void setState(State* state){  
  93.         this->state=state;  
  94.     }  
  95.     State* getHasQuarterState(){  
  96.         return hasQuarterState;  
  97.     }  
  98.     State* getNoQuarterState(){  
  99.         return noQuarterState;  
  100.     }  
  101.     State* getSoldState(){  
  102.         return soldState;  
  103.     }     
  104.     State* getSoldOutState(){  
  105.         return soldOutState;  
  106.     }  
  107.     int getCount(){  
  108.         return count;  
  109.     };  
  110.     void setCount(int numbers){  
  111.         this->count=numbers;  
  112.     };  
  113. };  
  114.   
  115. //具体状态中各个方法的具体实现。  
  116. //纸巾售完状态  
  117. void SoldOutState::insertQuarter(){       
  118.     cout<<"机器无纸巾,已退回硬币!"<<endl;  
  119. }  
  120. void SoldOutState::ejectQuarter(){  
  121.     cout<<"自动售货机根本没有硬币!"<<endl;  
  122. }  
  123. void SoldOutState::turnCrank(){  
  124.     cout<<"机器无纸巾,请不要操作机器"<<endl;  
  125. }  
  126. void SoldOutState::dispense(){  
  127. }  
  128. //没有投币状态  
  129. void NoQuarterState::insertQuarter(){         
  130.     tissueMachine->setState(tissueMachine->getHasQuarterState());  
  131.     cout<<"已投币!"<<endl;  
  132. }  
  133. void NoQuarterState::ejectQuarter(){  
  134.     cout<<"自动售货机根本没有硬币!"<<endl;  
  135. }  
  136. void NoQuarterState::turnCrank(){  
  137.     cout<<"请投币"<<endl;  
  138. }  
  139. void NoQuarterState::dispense(){  
  140. }  
  141. //有2元钱(已投币状态)  
  142. void HasQuarterState::insertQuarter(){  
  143.     cout<<"已投币!请不要重复投币!已退回重复投币!"<<endl;  
  144. }  
  145. void HasQuarterState::ejectQuarter(){  
  146.     tissueMachine->setState(tissueMachine->getNoQuarterState());  
  147.     cout<<"已取币!"<<endl;  
  148. }  
  149. void HasQuarterState::turnCrank(){  
  150.     tissueMachine->setState(tissueMachine->getSoldState());  
  151.     cout<<"请等待自动售货机出纸巾!"<<endl;  
  152. }  
  153. void HasQuarterState::dispense(){  
  154. }  
  155. //出售纸巾状态  
  156. void SoldState::insertQuarter(){  
  157.     cout<<"请等待自动售货机出纸巾!请不要投币!已退回投币!"<<endl;  
  158. }  
  159. void SoldState::ejectQuarter(){  
  160.     tissueMachine->setState(tissueMachine->getNoQuarterState());  
  161.     cout<<"请等待自动售货机出纸巾!无法取回已消费的硬币!"<<endl;  
  162. }  
  163. void SoldState::turnCrank(){  
  164.     cout<<"请等待自动售货机出纸巾!已响应你的操作!"<<endl;  
  165. }  
  166. void SoldState::dispense(){//售出纸巾动作  
  167.     if(tissueMachine->getCount()>0){  
  168.         tissueMachine->setState(tissueMachine->getNoQuarterState());  
  169.         tissueMachine->setCount(tissueMachine->getCount()-1);  
  170.         cout<<"你的纸巾,请拿好!"<<endl;  
  171.     }  
  172.     else{  
  173.         tissueMachine->setState(tissueMachine->getSoldOutState());  
  174.         cout<<"已退回你的硬币!纸巾已卖光,等待进货!"<<endl;  
  175.     }  
  176. }     
  177. //主函数  
  178. int main(){  
  179.     TissueMachine *tissueMachine=new TissueMachine(1);  
  180.     cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;  
  181.     tissueMachine->insertQuarter();//投币  
  182.     tissueMachine->turnCrank();//取纸巾  
  183.     cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;//不投币取纸巾测试  
  184.     tissueMachine->turnCrank();   
  185.     cout<<"纸巾数:"<<tissueMachine->getCount()<<endl;//售完纸巾,投币取纸巾测试  
  186.     tissueMachine->insertQuarter();    
  187.     tissueMachine->turnCrank();   
  188.     return 0;  
  189. }  


运行结果如下:

 

这里设置纸巾机一开始仅有1个纸巾,分别做不同的测试,可见纸巾自动售货机有不同的响应。

相关文章:

  • POJ2117-Electricity
  • HTML/CSS 知识点
  • java并发编程之:线程共享数据的方式
  • 《2017年全球数据库安全市场趋势》
  • 聊聊运维(1)证明你是坏运维的七个迹象,不要做CPR运维
  • 怎样制作C#安装程序
  • Sqoop_具体总结 使用Sqoop将HDFS/Hive/HBase与MySQL/Oracle中的数据相互导入、导出
  • [UWP]了解模板化控件(6):使用附加属性
  • 新公司,新挑战
  • linux: 进程管理常用指令
  • RabbitMQ6种常用业务场景分析
  • 面试题——敲代码推断操作系统位数
  • FetchType与FetchMode的差别
  • 4443: [Scoi2015]小秃玩矩阵|二分答案|匈牙利
  • OPENGL 红宝书实验笔记
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 2017前端实习生面试总结
  • ES6系列(二)变量的解构赋值
  • jquery cookie
  • orm2 中文文档 3.1 模型属性
  • Xmanager 远程桌面 CentOS 7
  • Yeoman_Bower_Grunt
  • 包装类对象
  • 笨办法学C 练习34:动态数组
  • 大主子表关联的性能优化方法
  • 高程读书笔记 第六章 面向对象程序设计
  • 高性能JavaScript阅读简记(三)
  • 给第三方使用接口的 URL 签名实现
  • 汉诺塔算法
  • 入职第二天:使用koa搭建node server是种怎样的体验
  • 鱼骨图 - 如何绘制?
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • (20050108)又读《平凡的世界》
  • (23)Linux的软硬连接
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (附源码)spring boot球鞋文化交流论坛 毕业设计 141436
  • (附源码)ssm基于jsp高校选课系统 毕业设计 291627
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET 材料检测系统崩溃分析
  • .NET 同步与异步 之 原子操作和自旋锁(Interlocked、SpinLock)(九)
  • .NET和.COM和.CN域名区别
  • .NET值类型变量“活”在哪?
  • .Net中的设计模式——Factory Method模式
  • @RequestParam @RequestBody @PathVariable 等参数绑定注解详解
  • [ JavaScript ] JSON方法
  • [ 数据结构 - C++] AVL树原理及实现
  • [].slice.call()将类数组转化为真正的数组
  • [2017][note]基于空间交叉相位调制的两个连续波在few layer铋Bi中的全光switch——
  • [Django ]Django 的数据库操作
  • [Flex][问题笔记]TextArea滚动条问题
  • [Head First设计模式]策略模式
  • [iOS开发]事件处理与响应者链
  • [java]删除数组中的某一个元素