转自: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。具体实现代码如下:

  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个方法,当然具体实现因状态不同而不同,具体代码如下:

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

运行结果如下:

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

[设计模式][c++]状态切换模式的更多相关文章

  1. Head First 设计模式 —— 12. 状态 (State) 模式

    思考题 public class GumballMachine { final static int SOLD_OUT = 0; final static int NO_QUARTER = 1; fi ...

  2. 【GOF23设计模式】状态模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_状态模式.UML状态图.酒店系统房间状态.线程对象状态切换 package com.test.state; public ...

  3. 【转】设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  4. 游戏开发设计模式之状态模式 & 有限状态机 & c#委托事件(unity3d 示例实现)

    命令模式:游戏开发设计模式之命令模式(unity3d 示例实现) 对象池模式:游戏开发设计模式之对象池模式(unity3d 示例实现) 原型模式:游戏开发设计模式之原型模式 & unity3d ...

  5. 设计模式 ( 十七) 状态模式State(对象行为型)

    设计模式 ( 十七) 状态模式State(对象行为型) 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ...

  6. 折腾Java设计模式之状态模式

    原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...

  7. 设计模式:状态(State)模式

    设计模式:状态(State)模式 一.前言     状态模式在某些场合中使用是非常方便的,什么叫做状态,如果大家学过<编译原理>就会明白DFA M和NFA M,在确定有限状态机和非确定有限 ...

  8. 北风设计模式课程---状态模式State(对象行为型)

    北风设计模式课程---状态模式State(对象行为型) 一.总结 一句话总结: 状态模式 具体状态的行为在具体的状态类中就解决,不用交给外部做判断.实质是将多条件判断弄成了多个类,在不同的类中做判断 ...

  9. 设计模式2——状态模式State

    参考链接: 设计模式之状态模式:https://www.cnblogs.com/haoerlv/p/7777789.html 设计模式系列之状态模式:https://www.jianshu.com/p ...

随机推荐

  1. selenium webdriver窗口切换(上)

    selenium webdriver窗口切换,有时候在做自动化的时候需要打开很多很多的页面, 当在操作不同的页面的时候需要切换窗口,下面是如何切换到前后页面窗口的操作: package test201 ...

  2. Look for the Air Jordan 32 in full family sizing

    Following the release of the 'Rosso Corsa' colorway, Jordan Brand is now set to officially launch th ...

  3. VUE路由去除#问题

    最近自己在写一个vue的小型管理系统,在浏览器中看到的路由都是带有#的,很是不好看.为了解决此问题,大家一般都会想到:mode: 'history'.可是在开发阶段没有问题,但是一旦build打包后, ...

  4. Linux root用户下不能打开Google-chrome的解决办法

    在root下打开chrome会出现no sandbox的错误 解决方案: 1.找到google-chrome文件 在目录/opt/google/chrome 下 2.使用gedit打开该文件 最后一行 ...

  5. 3/5/2014 cfb 小心

    During each move the player can choose all lines of the matrix where dwarf is not on the cell with c ...

  6. 全局监听SCREEN_ON和SCREEN_OFF的替代方法--监听屏幕解锁事件

    在做一个程序的时候,需要时刻保持某一服务是启动的,因此想到了通过监听屏幕SCREEN_ON和SCREEN_OFF这两个action.奇怪的是,这两个action只能通过代码的形式注册,才能被监听到,使 ...

  7. Python2 和Python3 的差异总结

    一.基本语法差异 1.1 核心类差异 Python3对Unicode字符的原生支持 Python2中使用 ASCII 码作为默认编码方式导致string有两种类型str和unicode,Python3 ...

  8. python在交互模式下直接输入对象后回车,调用的是对象的__repr__()方法,这个方法表示的是一个编码,用print+对象是调用对象的__str__方法

    交互模式下调用对象的__repr__()方法,这个方法表示的是一个编码 >>> u"国庆节快乐"u'\u56fd\u5e86\u8282\u5feb\u4e50' ...

  9. 如何向GLSL中传入多个纹理

    http://blog.csdn.net/huawenguang/article/details/41245871 如何向GLSL中传入多个纹理 这几天在研究如何实现用GLSL对多个纹理进行融合处理, ...

  10. TimeUnit简析

    TimeUnit是java.util.concurrent包下面的一个类,主要有两种功能: 1.提供可读性更好的线程暂停操作,通常用来替换Thread.sleep() 2.提供便捷方法用于把时间转换成 ...