转自: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. NLP总览

    一.自然语言处理概述 1)自然语言处理:利用计算机为工具,对书面实行或者口头形式进行各种各样的处理和加工的技术,是研究人与人交际中以及人与计算机交际中的演员问题的一门学科,是人工智能的主要内容. 2) ...

  2. iOS 开发笔记-UILable/UIFont/UIButton常见设置

    UILabel的常见设置 @property(nonatomic,copy) NSString *text; 显示的文字 @property(nonatomic,retain) UIFont *fon ...

  3. 终端FQ

    前言我一直以为全局FQ就是所有的都能够自由访问,没想到终端有时候却不行,这里终端一般指的是window的cmd和mac的Terminal.终端一般程序员用的比较多,下载第三方依赖啊,都需要,所以我总结 ...

  4. 数据仓库基础(七)Informatica PowerCenter介绍

    本文转载自:http://www.cnblogs.com/evencao/p/3140938.html  Infromatica PowerCenter介绍: 1993年在美国加利福尼亚州成立,一年后 ...

  5. MIPSsim使用说明

    MIPSsim下载:https://files.cnblogs.com/files/jiangxinnju/MIPSsim.zip 启动模拟器 双击MIPSsim.exe,即可启动该模拟器.MIPSs ...

  6. 拉取远程仓库到本地错误The authenticity of host 'github.com (13.229.188.59)' can't be established.

    1.个人在github上面创建了仓库,通过本地的git拉取远程仓库到本地报错信息如下: 这是因为Git使用SSH连接,而SSH第一次连接需要验证GitHub服务器的Key.确认GitHub的Key的指 ...

  7. MySQL Crash Course #07# Chapter 15. 关系数据库. INNER JOIN. VS. nested subquery

    索引 理解相关表. foreign key JOIN 与保持参照完整性 关于JOIN 的一些建议,子查询 VS. 联表查询 我发现MySQL 的官方文档里是有教程的. SQL Tutorial - W ...

  8. c++第十六天

    p99~p101: 1.迭代器的指向:容器内的元素或者尾元素的下一位. 2.迭代器与整数相加减返回值为移动了若干位置的迭代器. 3.迭代器间相加减返回值则是类型为 difference_type 的带 ...

  9. 20145118《Java程序设计》 第9周学习总结

    20145118 <Java程序设计>第9周学习总结 教材学习内容总结 1.SUN公司定义了一套Java操作数据库的规范(接口)来简化数据库操作,称之为JDBC.开发人员只需要学习jdbc ...

  10. 20145329 《网络对抗技术》Web基础

    实践目标 Web前端HTML 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML. Web前端javascipt 理解JavaScript的基本 ...