转自: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 plaincopy 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 plaincopy 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. 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个纸巾,分别做不同的测试,可见纸巾自动售货机有不同的响应。

转载于:https://www.cnblogs.com/lyggqm/p/7064565.html

[设计模式][c++]状态切换模式相关推荐

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

    设计模式之状态(State)模式 在软件开发过程中,应用程序中的有些对象可能会根据不同的情况做出不同的行为,我们把这种对象称为有状态的对象,而把影响对象行为的一个或多个动态变化的属性称为状态.当有状态 ...

  2. 策略模式和工厂模式的区别_java设计模式之状态模式,策略模式孪生兄弟

    状态模式 状态模式(State Pattern)中,类的行为是基于它的状态改变的,状态之间的切换,在状态A执行完毕后自己控制状态指向状态B,状态模式是不停的切换状态执行.这种类型的设计模式属于行为型模 ...

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

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

  4. python的out模式_Python设计模式之状态模式

    状态模式 面向对象编程着力于在对象交互时改变它们的状态.在很多问题中,有限状态机(通常名为状态机)是一个非常方便的状态转换建模(并在必要时以数学方式形式化)工具.首先,什么是状态机?状态机是一个抽象机 ...

  5. 码农小白 设计模式篇 状态模式

    码农小白 设计模式篇 状态模式 1.状态的认识 2.面向过程思维的代码实现 小结: 3.面向对象实现 小结 4.用状态模式实现 1.状态模式的简介 2.状态模式的好处 3.状态模式的使用 4.代码实现 ...

  6. C++设计模式之状态模式(state)(行为型)

    一 定义 状态模式:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类. 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况.把不同状态的操作分散到不同的状态对象 ...

  7. 秒懂设计模式之状态模式(State Pattern)

    [版权申明] 非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/116375477 出自:shusheng0 ...

  8. 设计模式之状态模式(State)

    什么是状态? 我们在购物网站进行购物时,订单会产生几种状况:已下单.已付款.送货中.确定收货等状态. 所以系统会判断该订单的状态,不管是哪种状态都应给出对应的操作,这就是状态. 什么是状态模式? 在软 ...

  9. Android设计模式之状态模式

    状态模式定义 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式的使用场景 1.一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为. 2.代码中包含大 ...

最新文章

  1. python实现socket编程,服务端开启多线程(和多个客户端聊天)
  2. Linux高级存储管理
  3. 计算机中英语GAI缩写,等等英语_英语中“等等”缩写成为etc吗要加一点吗全拼是什么谢谢大家_淘题吧...
  4. Visual C++ 确定要使用的链接方法
  5. [转] Jenkins实战演练之Windows系统节点管理
  6. android ProgressBar 自定义进度条颜色
  7. (2) java项目中用redis
  8. 数学之路(3)-机器学习(3)-机器学习算法-SVM[9]
  9. cstring判断包含字符串_作为java程序员要知道的大厂常见的算法面试题:字符串的包含...
  10. 编译器--简单数学表达式计算器(一)
  11. 设计模式--代理模式Proxy(结构型)
  12. HTML 制作钓鱼网站实现跳转(简篇)
  13. 在LCD液晶屏成功显示图片
  14. 《商业俏佳人》游戏体验文档
  15. 台式电脑如何使用无线网,wifi怎么连接?
  16. linux如何修改当前时间
  17. 计算机网络思维导图 (免费下载)
  18. linux批量卸载rpm,shell实现rpm -e 一键卸载所有相关包以及依赖
  19. CAN总线的前世今生
  20. 新ICT时代下,看华为OneAir行业无线专网如何引领工业物联新风尚

热门文章

  1. python itertools模块位置_Python高效编程之itertools模块详解
  2. 程序员的数学全三册密码_阿波罗50年前成功登月,少不了这位硬核女程序员
  3. pythonsuper多重继承_Python多重继承引发的问题——牛逼的super
  4. 查看MySQL的版本
  5. 前端框架:layui
  6. java获取数据库MetaData
  7. 可爱的python_《可爱的Python》读书笔记
  8. 小米笔记本服务器系统,小米笔记本Pro GTX版
  9. usb3.0 ssd 测试软件,当USB3.0遇上SSD,TB败得移动硬盘盒测试
  10. mac catalina删除系统多余文件 内存不足_macOS Catalina Patcher(如何在旧mac上安装Catalina系统)...