程序设计模式 —— State 状态模式

我应该如何阅读?



本文将使用优雅的文字风格来告诉你什么是状态模式。

注意:

  1.在阅读本文之前请保证你已经掌控了 面对对象的思想与 多态的基本概念,否则将难以理解。

  2.本文实现将用C++实现,你不一定必须学会C++,这些代码都有非常丰富的注释和简单的语法。

  3.请不要跳着看,除非你已经学会。

那么现在开始吧



或许你会以为是类的各种状态,其实差不多。但是还有各种行为

允许一个对象在其内部状态改变时改变它的行为。实例化的对象看起来似乎修改了它的类

首先,状态,顾名思义你应该就差不多明白。它可以表示一个类中的状态,但是不仅仅只是状态。

它也包括了 行为,有点儿类似于策略模式,但是又不是策略模式。

  我一直坚信。这个设计模式,只需要仅仅一张图片即可完成说明:这张三个片段代表飞机的三个不同状态

你懂了吗?根据日常生活的常识,你应该明白飞机这几种状态,但是我相信你应该还不太明白如何实现具体代码。

  你也应该明白 “没有动力” 的时候是不能飞行的。

  你也应该明白 “正在飞行” 的时候是不能再飞行的。

正是因为如此,我们就可以直接在不同的状态子类,实现不同的方法,比如 “没有动力” 的时候,如果使用 “起飞” 成员函数,将会提示没有动力,起飞失败。

而 “没有飞行” 的时候却是可以 使用 “起飞” 成员函数。

那么我应该具体怎么做?



实现的代码很简单。并且有丰富的注释,希望你能理解。

大致框架图:

飞机类是可以使用 任何一个 状态类,表示不同的状态。

注意:如果你不会 C++,也没有关系,”Virtual” 关键字你可以无视,至于成员函数后面有一个 "=0",你可以理解为抽象方法,子类必须实现它,用Java的思想去看待代码

状态类的基类

下面我们来具体实现:基类永远都是这么简单,对吧?

 1 /* 状态类的基类 抽象类*/
 2 class State
 3 {
 4 public:
 5     State()
 6     {
 7
 8     };
 9     virtual ~State()
10     {
11
12     };
13     virtual void fly()=0;    //起飞   =0是纯虚函数,可以理解为子类必须实现的 抽象方法
14     virtual void stopfly()=0;    //停止飞行,降落
15     virtual void up()=0;        //拉升飞行高度
16     virtual void down()=0;        //拉低飞行高度
17
18 };

实现状态类的各个子类

然后我们应该来实现 各种状态了

代码有点多,但是都是很容易的。而且有丰富的注释。。

  1 //状态类: 正在飞行 状态类
  2 class StateFlying : public State
  3 {
  4 public:
  5     StateFlying()
  6     {
  7
  8     };
  9     virtual ~StateFlying()
 10     {
 11
 12     };
 13     virtual void fly()
 14     {
 15         //因为飞机正在飞行,所以不能再度起飞
 16         cout << "ERROR: Can‘t do this!! You are not ‘stopfly‘! " <<endl;
 17     };    //起飞
 18     virtual void stopfly()
 19     {
 20         //正在飞行的时候当然可以降落
 21         cout << "INFO: OK! stopfly" << endl;
 22     };    //停止飞行,降落
 23     virtual void up()
 24     {
 25         //目前的状况当然可以这样做
 26         cout << "INFO: OK! up" << endl;
 27     };    //拉升飞行高度
 28     virtual void down()
 29     {
 30         //目前的状况当然可以这样做
 31         cout << "INFO: OK! down" << endl;
 32     };    //拉低飞行高度
 33 };
 34
 35 //状态类: 停机状态
 36 class StateNotFly : public State
 37 {
 38 public:
 39     StateNotFly()
 40     {
 41
 42     };
 43     virtual ~StateNotFly()
 44     {
 45
 46     };
 47     virtual void fly()
 48     {
 49         //因为是 停机状态,所以是可以起飞的
 50         cout << "INFO: OK! " << endl;
 51     };    //起飞
 52     virtual void stopfly()
 53     {
 54         //你难道可以 停机之后 再停机么?
 55         cout << "ERROR: Can‘t do this!! You are not ‘Flying‘! " <<endl;
 56     };    //停止飞行,降落
 57     virtual void up()
 58     {
 59         //停机的时候这些空中动作就算了吧
 60         cout << "ERROR: Can‘t do up!! You are not ‘Flying‘! " <<endl;
 61     };    //拉升飞行高度
 62     virtual void down()
 63     {
 64         //停机的时候这些空中动作就算了吧
 65         cout << "ERROR: Can‘t do down!! You are not ‘Flying‘! " <<endl;
 66     };    //拉低飞行高度
 67 };
 68
 69 //状态类: 没有动力状态
 70 class StateNotPower : public State
 71 {
 72 public:
 73     StateNotPower()
 74     {
 75
 76     };
 77     virtual ~StateNotPower()
 78     {
 79
 80     };
 81     virtual void fly()
 82     {
 83         //没有动力了,无法起飞!
 84         cout << "ERROR: Can‘t do fly!! You are not Power!  " <<endl;
 85     };    //起飞
 86     virtual void stopfly()
 87     {
 88         //没有动力了,赶紧降落
 89         cout << "INFO: OK! stopfly" << endl;
 90     };    //停止飞行,降落
 91     virtual void up()
 92     {
 93         //没有动力,你是没法爬升的,等死吧!
 94         cout << "ERROR: Can‘t do up!! You are not Power!  " <<endl;
 95     };    //拉升飞行高度
 96     virtual void down()
 97     {
 98         //没有了动力了,但是可以 滑翔下降
 99         cout << "INFO: OK! down" << endl;
100     };    //拉低飞行高度
101 };
102
103 /*
104 如果你学过其他设计模式,或者了解代码复用,你可能会说上面的代码可以复用(我是指各个成员函数中的输出),完全不必要这样写。
105 这里仅仅只是为了举例,尽量少的牵扯到其他。
106  */

这样,我们的三个状态就好了。从代码中你也可以看见一件事情,每个状态都有自己的各自的具体处理。

但是你现在可能会问,为什么要浪费时间写这么多看起来都是差不多的功能呢?

其实这样的话,你在飞机类里面就会出现一种现象,就是需要一个个判断当前的状态然后做相应的事情。类似于这样:

 1 //假设你要执行停飞,但是不使用状态模式,将会有这种代码的出现
 2 if(/*当前不是飞行ing*/)
 3 {
 4     //提示不能停飞
 5 }
 6 if(/*当前是飞行*/)
 7 {
 8     //执行停飞
 9 }
10 if(/*当前没有动力*/)
11 {
12     //不能停飞
13 }

这种代码不但枯燥无趣,而且难以维护。如果哪天你加了一个“即将坠机”的状态,你需要修改每个类的每个方法。。。

实现飞机类 AirPlane 类

好了,回归正题,我们来实现飞机!看看飞机是如何不需要进行 状态 判断就可以直接用:

 1 /*这是 飞机类  这个类才是正在 在用上面的各种类*/
 2 class AirPlane
 3 {
 4 private:
 5     string name;
 6     State *state;    //状态
 7
 8 public:
 9     AirPlane()
10     {
11         this->state = new StateNotPower();    //初始化状态为 “没有动力” 状态
12     };
13     ~AirPlane()
14     {
15         delete this->state;    //记得释放喔~
16     };
17     void setName(const char* name)
18     {
19         this->name = name;    //复制
20     }
21     void setState(State * state)
22     {
23         delete this->state;    //先释放原来的
24         this->state = state;    //改成新的 状态
25     };
26     void fly()
27     {
28         cout << "[AirPlane][" << this->name << "] ";
29         this->state->fly();
30     };    //起飞
31     void stopfly()
32     {
33         cout << "[AirPlane][" << this->name << "] ";
34         this->state->stopfly();
35     };    //停止飞行,降落
36     void up()
37     {
38         cout << "[AirPlane][" << this->name << "] ";
39         this->state->up();
40     };        //拉升飞行高度
41     void down()
42     {
43         cout << "[AirPlane][" << this->name << "] ";
44         this->state->down();
45     };        //拉低飞行高度
46     //你可以发现,我们将具体的实现全部交给了 子类去实现,我们完全不必管到底如何进行。
47     //当然了,如果你会策略模式,或许你能更加灵活运用,策略模式与本模式很相似。
48 };

你可以看见,我们已经将方法全部委托给了 状态 类。对吧。这有什么问题?

你根本无需在乎飞机到底是什么状态,完全就是直接用 状态类 的方法就好了。

现在我们来战斗吧



“作为 Pattern Art Online (AR)空战游戏的 元老玩家。你接到上级命令需要赶快去前线实行救援,你的队友几乎已经死绝了,你感到万分悲伤,你决定报仇”

“但是 你本来就很少上前线,因为你已经普升到 军长了,但是你手上已经没有任何战斗飞行员了,你必须亲自上阵,因为如果一旦退缩的话,后面就是你的家乡”

 1 #include "State.h"
 2
 3 using namespace std;
 4
 5 int main(int argc, char const *argv[])
 6 {
 7     /*
 8         你实例化了一架 新的飞机,这个时候 作为 Pattern Art Online 游戏的 元老玩家。
 9         你需要赶过去支援你的 队友!
10         前线目前已经非常紧张,导致你起飞的动作僵硬无比。
11     */
12     /*好的,我创建了一架飞机*/
13     AirPlane *air = new AirPlane();
14     /*随便取个名字,Alier Two 号*/
15     air->setName("Alier Tow");
16     /*起飞吧!我要赶快去 救我的队友*/
17     air->fly();    /*输出: ERROR: Can‘t do fly!! You are not Power!*/
18     /*该死,太紧张了,毕竟时间紧,你,就你,快加满油!*/
19     air->setState(new StateNotFly());    //状态改成一切就绪,但没有正在飞行
20     /*很好,油满了,给我拉升!*/
21     air->up();    /*输出: ERROR: Can‘t do up!! You are not ‘Flying‘!*/
22     /*可恶,忘记先发动 螺旋桨了。。。*/
23     air->fly();    /*输出: INFO: OK!*/
24     /*哈哈哈,飞起来了!*/
25     air->setState(new StateFlying());    //状态改成 飞行ing
26     /*给我飞高点*/
27     air->up();    /*输出: INFO: OK! up*/
28
29     /* ---五个小时的飞行后---*/
30
31     /*咦?怎么回事,螺旋桨不转了?*/
32     air->setState(new StateNotPower());
33     /*卧槽,忘记带后备用油了,我可是要飞到 北美洲去啊!总部,总部!请求加油机支援!!!*/
34     /*可恶。给我飞高点!!!*/
35     /* ---距离地面还剩 800米--- */
36     air->up();    /*输出: ERROR: Can‘t do up!! You are not Power!*/
37     /*不...不要啊!不管什么,给我飞起来啊!!!*/
38     air->fly();    /*救命啊: ERROR: Can‘t do fly!! You are not Power!*/
39     /* ---即将坠机--- */
40     /* ...砰... */
41     /* --- You die--- */
42
43     cin.get();
44
45     delete air;        /*可怜的你,还没有飞到前线就坠机了,真是.....*/
46     return 0;
47 }

输出:

那么,你现在明白了?状态模式。你可以轻松的复用。甚至可以定义N个状态,定义个N不同的飞机,并且飞机与状态这之间却可以轻松的反复重用!

因为状态都继承了 State 抽象类,而飞行则只管使用,定义状态,而不需要管理不同状态下的不同行为。

就这样结束了?



“你的队友对你十分失望,你的上司对你的飞行技术感到质疑,这是一个不好的现象,这意味着你很有可能要被逐出 【Fly!Fly!Fly!】战队。”

“那么。你不准备做什么?”

“听说 Pattern Art Online (VR)角色扮演类游戏开始了,你的队友貌似挺感兴趣,你决心要利用黑客技术来帮助自己取得队友的信任。

故事后续情节请看:《程序设计模式——策略模式》

最后



  但是要注意的一点,不是说有了这个模式,就必须要加进去使用,程序会更棒。

设计模式要与你的程序相互和谐,不能写个 “HelloWorld” 程序都用到了设计模式。

总的一句话,设计模式不是规则,而是你随时可以改变的模式。这也是很多设计模式书籍强调的一点。

不论对你是否有帮助,还是谢谢您的耐心查看。如有错误之处,还望指教。

时间: 2024-12-17 04:20:27

程序设计模式 —— State 状态模式的相关文章

[C++设计模式] state 状态模式

<head first 设计模式>中的例子很不错,想制造一个自动交易的糖果机,糖果机有四个状态:投入钱不足,投入钱足够,出售糖果,糖果售罄.糖果机的当前状态处于其中不同的状态时,它针对同一个操作的反映动作也不同.传统面向过程编程会套用if-else对不同的状态下分别处理,逻辑麻烦而且不具有可拓展性. 状态模式:允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类.状态模式的重点在于状态转换,很多时候,对于一个对象的状态,我们都是让这个对象包含一个状态的属性,这个状态属性记录着

JAVA设计模式之 状态模式【State Pattern】

一.概述 当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式.状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化.状态模式是一种对象行为型模式. 二.适用场景 用于解决系统中复杂对象的多种状态转换以及不同状态下行为的封装问题.简单说就是处理对象的多种状态及其相互转换. 三.UML类图 四.参与者 1>.AbstractState(抽象状态类): 在抽象状态类中定义申明了不同状态下的行为抽象方法,而由子类

设计模式 ( 十八 ):State状态模式 -- 行为型

1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出不同的处理.最直接的解决方案是将这些所有可能发生的情况全都考虑到.然后使用if... ellse语句来做状态判断来进行不同情况的处理.但是对复杂状态的判断就显得“力不从心了”.随着增加新的状态或者修改一个状体(if else(或switch case)语句的增多或者修改)可能会引起很大的修改,而程序的可读性,扩展性也会变得很弱.维护也会很麻烦.那么我就考虑只修改自身状态的模式. 例子1:按钮来控制一个电梯的状态,一个电梯开们,关门,停,

详解state状态模式及在C++设计模式编程中的使用实例

原文地址:http://www.jb51.net/article/80713.htm 这篇文章主要介绍了state状态模式及在C++设计模式编程中的使用实例,在设计模式中策略用来处理算法变化,而状态则是透明地处理状态变化,需要的朋友可以参考下 每个人.事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State).最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过.在出口处也是验票,如果正确你就可以 ok,否则就不让你通

设计模式21:State 状态模式(行为型模式)

State 状态模式(行为型模式) 动机(Motivation) 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态的行为就可能完全不同. 如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转换之间引入紧耦合? 意图(Intent) 允许一个对象在其内部状态改变时改变它的行为.从而使对象看起来似乎修改了其行为.——<设计模式>GoF 结构(Structure) 示例代码 public enum Document

设计模式之状态模式(State)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式---状态变化模式之state状态模式(State)

前提:状态变化模式 在组建构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定?“状态变化”模式为这一个问题提供了一种解决方案. 典型模式 状态模式:State 备忘录模式:Memento 一:State状态模式 (一)概念 允许一个对象在其内部状态改变时改变它的行为.对象看起来似乎修改了它的类 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. (二)动机 在软

设计模式之状态模式

<设计模式>对状态模式的定义:允许一个对象在其状态改变时,改变它的行为.看起来对象似乎修改了它的类.别名:状态对象(Objects for State). 在下面两种情况下均可以使用State模式: 1 一个对象的行为取决于它的状态,并且他必须在运行时刻根据状态改变它的行为. 2 一个操作中含有庞大的多分枝的条件语句,并且这些分支依赖于该对象的状态.这个状态通常用一个或多个枚举常量表示.通常,有多个操作包含这一相同的条件结构.State模式将每一个条件分支放入一个独立的类中.这是得你可以根据对

设计模式之: 状态模式

什么是状态设计模式 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化. 什么时候使用状态模式 对象中频繁改变非常依赖于条件语句. 就其自身来说, 条件语句本身没有什么问题(如switch语句或带else子句的语句),不过, 如果选项太多, 以到程序开始出现混乱, 或者增加或改变选项需要花费太多时间, 甚至成为一种负担, 这就出现了问