转自 : http://blog.csdn.net/wishfly/archive/2008/01/22/2060026.aspx

STATE模式的中文名称是状态模式。在《设计模式》一书中的定义是:允许一个对象在其内部状态改变的时候改变它的行为。对象看起来似乎修改了它的类(中文译书上的原话,不过我觉得这句话应该翻译成——对象显现出来的是改变了它所属的类)。看了定义还是感觉有些抽象,好的,我们还是通过一个例子来学习该模式。还是先从情节设计开始:
严风吹霜海草凋,筋干精坚胡马骄。 汉家战士三十万,将军兼领霍嫖姚。 流星白羽腰间插,剑花秋莲光出匣。 天兵照雪下玉关,虏箭如沙射金甲。 云龙风虎尽交回,太白入月敌可摧。 敌可摧,旄头灭,履胡之肠涉胡血。 悬胡青天上,埋胡紫塞旁。 胡无人,汉道昌。

李白这首诗豪气万丈的诗篇,描述的是汉骠骑将军霍去病率领大军,出陇西,突袭匈奴部落的战争场面,千载之下,犹让人感觉豪迈异常。匈奴和汉朝的争端从汉高祖刘邦开始。楚汉之争过后,刘邦统一了中国。此时,匈奴部族也被冒顿(这两字发音应为mo du)单于统一了起来。匈奴逐渐强大的同时,开始窥觑汉朝的疆土。冒顿单于领军进犯太原,包围晋阳。刘邦亲率30万大军进击匈奴,不期竟中匈奴之计,被困于白登。幸亏用陈平之计,送重礼于冒顿夫人,走枕边风路线,才让匈奴大军让开一条路,逃了出来,狼狈之极。这场战役的结果就是汉朝采取和亲政策,送汉宗室之女给匈奴王为妾,并借机进贡。但这也避免不了匈奴侵犯汉族边界,杀戮汉民,抢夺牲畜。这样的历史持续了数十年,直到汉武帝的时候还保持着。而雄才大略的武帝,岂能容忍此奇耻大辱。坚毅勇猛的他,筹划着对匈奴的打击,经过一系列的改革(个人觉得他改革兵种,将以车兵步兵为主的军队改为轻骑兵为主,对这场战役意义最为重大),开始了对匈奴的穷追猛打。这段历史造就了中国历史上两颗耀眼的将星:卫青,霍去病。霍去病更加夺目,他18岁的时候,随卫青大军出征。他率领800轻骑,远离大军,奔赴敌人腹地,斩敌二千余人。被汉武帝封为冠军侯。后又率1万大军出陇西,转战千余里,斩获8000于人。再于同年秋天,出北地,攻祁连山,大获全胜,斩获三万余人。汉朝与匈奴的最后一次大型战役也由卫青,霍去病完成。霍去病亲率5万骑兵远离代郡,对匈奴人穷追猛打,歼敌七万余人。乘胜追击到狼居胥山(今在蒙古境内),并在此举行祭天仪式(封狼居胥)。此时的霍去病仅仅21岁,但二年后英年早逝。在他短暂的一生中四次与匈奴做战,转战数千余里,灭敌十余万人,彻底熄灭了匈奴人的嚣张气焰,也给大汉的边境带来了安宁。不仅如此,他还留下了“匈奴未灭,何以家为”的千古豪言。真是铮铮男儿形象,为万世铁骨男儿楷模。
    前年看到的一部连续剧《汉武大帝》,总体感觉还行。不过太过小气,为了一些戏剧效果,把很多历史事件强塞了进去。如曹操为人捉刀的故事,被安排到了汉武帝身上;为了那么一段冒顿单于鸣镝杀父的情节,让他晚生了数十年,居然他还成了汉武帝的死敌(为汉武帝安排这么一个对手,我总觉得太小觑了汉武帝)。
    还有些事,本不想写了,后来想想还是写吧。反正咱这是乱砍,就随性而来吧。网上有个臭名昭著的网站,叫做汉网。集聚着一群民族主义分子,提倡汉服(不过他们好像也不是提倡每天都穿),说这样可以帮助中国人恢复民族自尊心。让我想起阿Q说过的:老子原前也阔过。还有明显意淫古人的意味,翻看历史,我们可以看到武帝之前,汉族一直受匈奴人的欺凌。而武帝之后,国力衰落,虽然周边的小国还算顺服,但汉朝还是失去了武帝时的雄霸之气。试想想如果当时情况下的皇帝不是汉武,而是一个软弱无能,如明朝的那个建文帝,估摸着汉朝内部都搞不定。更甭谈打击匈奴了,而消灭匈奴取得成功,也和武帝破格提拔的将领有关。试想没有卫青,霍去病又会是什么情况?第一次攻击匈奴,四路人马出击,两路被败(著名的飞将军李广被俘,而后逃脱),一路无功,只有卫青有所斩获。而后的多次出击中,除卫青,霍去病外的其他将领多有败绩。在电视《汉武大帝》中,李广的死被安排成了为了成全卫青的计策,吸引匈奴,他主动陷于匈奴包围圈中,英勇战死(这段情节,描写的匈奴人也太弱智了)。我一哥们看到这情节后,哈哈大笑。然后他津津有味的为别人讲解李广真正的死因:李广是路痴死的。李广出击匈奴的战争中多次迷路,最后一次因为卫青派人问他迷路的原因,李广自杀身亡。李广是汉朝名将,但我觉得他不适合那个年代。从战国时代起,名将都以智略取胜。因为战争基本都以攻城拔地的形式进行,都有相对固定的战争场地。武器也以战车,重骑兵为主,行动较为缓慢,主要打的是阵地战。而当时匈奴,采取的是打得过就打,打不过就跑得游击套路(呵呵,应该是我军游击战的雏形了)。面对这样的对手,更需要的就是霍去病这种有锐气,深沉勇猛,敢于追着敌人背后猛打的将领(我觉得国民党将领薛岳也属于此类战将)。当汉武让霍去病学习古兵法的时候,他的回答是:顾方略何如耳,不至学古兵法。也许正是这种藐视权威的气概成就了霍去病,也成就了“犯大汉者,虽远必诛”的大汉豪情。但这段历史的演绎里离不开汉武帝,也离不开霍去病(战功最著的是他,单靠卫青可能这场战争很难如此彻底),是他们的组合成就了这段历史。没有这样的组合,也许大汉天下会被匈奴的铁蹄践踏,而汉文明也许会象希腊文明那样被游牧民族践踏而消亡。历史充满了偶然性,历史本身并不值得我们拿来炫耀。我觉得如今的中国人没有什么不自信的,我也不觉得现在的中国人没古代好,伟大的时代总离不开伟大的人物。也许我们的民族只是现在缺少一个这样的人物,那也就不好怨天尤人了,因为身处这个时代的我们,没有一个是伟人,我不是,你也不是,那你怪谁去?好了,不扯了,还是认真做我们的Coding生涯吧。
    我们以骠姚将军深入漠北追歼匈奴人的情节作为我们的例子,这次我们将五万骑兵作为我们的对象。这五万骑兵作为一个作战整体,会有一些这样的一些状态:休整,前进,攻击。整个作战过程中都在这些状态下进行着。我们先来看看下图:

这里我们看到一个转换图:共有三种状态Advaneing(前进),Assaulting(攻击状态),Resing(休息状态)。三个状态之间可以进行转换,Advaneing和Resing可以直接转换,Advanceing和Resing可以直接转换到Assaulting状态,而Assaulting只能在敌军被全歼的时候才能转化到Resing状态。
    如何来实现该系统呢?我们还是首先用土鳖似的方式来实现第一个系统,这样更有助于看到模式的优点。我们首先建立一个Army类,如下:

#define ADVANEING   0 
#define ASSAULTING  1 
#define RESTING      2

Class Army 
{  
Private: 
    int m_iState;      
    int m_iEmptyCout;    
public:  
    Army():m_iState(RESTING)
    {
    }    
    
    Void Advance();    
    Void Assault();   
    Void Rest();   
};

Void Army::Advance() 
{

If (m_iState ==  ADVANEING   )  
    {      
        Cout << “Are Advaning!” << endl; 
    } Eles if ( m_iState == ASSAULTING ) 
    {  
        Cout << “sorry! Are assauling!Can’t Advace” << endl; 
    }Else if( m_iState == RESTING ) {  
        m_iState = ADVANING  Cout << “ok!Go!” << endl;
    } 
}

Void Army:: Assault () 
{

If (m_iState ==  ADVANEING   )
    {

m_iEmptyCout = 100;
        m_iState == ASSAULTING;   
        Cout << “ok!Assault!” << endl;
    } Eles if ( m_iState == ASSAULTING ) { 
        m_iEmptyCount -= 100;  
        Cout << “Are assauling!” << endl;

}  Else if( m_iState == RESTING ) {
        m_iEmptyCout = 100;  m_iState = ASSAULTING; 
        Cout << “ok! Assault!” << endl;
    }
}

Void Army:: Rest () 
{

If (m_iState ==  ADVANEING   )     
    { 
        m_iState == RESTING;  
        Cout << “ok!Rest!” << endl;
    } Eles if ( m_iState == ASSAULTING ) { 
        Cout << “Are assauling!can’t Rest” << endl;
    }  Else if( m_iState == RESTING ) { 
        Cout << “Are Resing!” << endl;
    }

}

好了这样我们的类就完成了,虽然看起来有些杂乱,但运行应该没有什么问题。这样完成虽说土了一些,但事实上并不影响它的正常运行。但我们需要考虑的一个问题是:当需求变化的时候,我们的程序该如何去改?软件界的一个规律就是需求一直在变更,变更伴随着软件的生存到死亡的过程。如今流行的设计模式,重构,测试驱动开发等技术目的都是为了适应需求的变更,而将程序修改的难度降到最低来。所以我们来考虑这样的情况,由于骠骑将军取得了大胜,举行了祭天仪式,祭天仪式中战士兴奋度提高,杀敌热情暴增。所以骠骑决定将这个仪式加入到战斗安排中,当取得胜利的时候,举行祭天仪式。而这又是一个新的状态,该状态只有在Rest状态下才能切换过去,我们该如何去修改程序呢?以目前的做法,我们需要在每个函数中添加条件,修改函数,这样又与我们在策略模式中提到的规则“一个模块对扩展应该是开放的,而对修改应该是关闭的”背道而驰了。怎么解决呢?还是同样的方法:提炼出一个类。同样为了解决动态改变状态的需求,我们还应该记着另一个规则:尽量针对接口编程,而不要针对实现编程。闲言少叙,我们还是看类图,这样来的快一些。

从类图,我们可以看到Army类中拥有一个Station类的对象,它所有的操作将通过该对象来实现。是不是发觉和策略模式很相似?先不说这个,我们先看完例子再说。看看具体代码:
    我们首先看以下State接口,很简单,就是几个纯虚函数。

class State 
{
public: 
    virtual void Advance() = 0; 
    virtual void Assault() = 0; 
    virtual void Rest() = 0;  
    virtual void Fiesta() = 0; 
};

我们再来看一下AdvanceState,我们看到在AdvanceState中有一个Army对象的指针,是因为需要在内部修改状态,具体的代码中可以看到。

很简单了,就是根据当前状态来处理各个函数。我们看到有这样的函数m_pArmy->SetState(m_pArmy->GetRestState());是用来修改Army所处的状态的。在Army类中,我们可以看到它的具体实现。其它几个状态类的实现类同,就不房到这里了,感兴趣的可以到附件中自己找。
    我们再来看看Army类的定义:

class State;  
class Army

private: 
    State* m_pState;   
    
    //保存各个状态指针便于使用,当有新的状态填加的时候,我们也需要在此处添加  
    State* m_pAdvanceState;  
    State* m_pAssaultState; 
    State* m_pRestState;  
    State* m_pFiestaState;  
    int m_iEmptyCount; 
public:

Army();    
    void SetState(State *pState); 
    State* GetAdvanceState();  
    State* GetAssaultState(); 
    State* GetRestState(); 
    State* GetFiestaState();

void Advance();  
    void Assault();  
    void Rest(); 
    void Fiesta();

void SetEmptyCount(int iEmptyCount)
    {
        m_iEmptyCount = iEmptyCount;
    }  
    
    int GetEmptyCount()
    {
        return m_iEmptyCount;
    } 
};

它的实现:

Army::Army() 
{   
    m_pAdvanceState = new AdvanceState(this); 
    m_pAssaultState = new AssaultState(this);  
    m_pRestState = new RestState(this);  
    m_pFiestaState = new FiestaState(this); 
    m_pState = m_pRestState;   
    m_iEmptyCount = 0;
}

void Army::SetState(State *pState) 
{    m_pState = pState; 
}

State* Army::GetAdvanceState() 
{
    return m_pAdvanceState;
}

State* Army::GetAssaultState()
{
    return m_pAssaultState;
}

State* Army::GetRestState() 
{
    return m_pRestState;
}

State* Army::GetFiestaState() 
{
    return m_pFiestaState;
}

void Army::Advance() 
{
    m_pState->Advance();
}

void Army::Assault()
{
    m_pState->Assault();
}

void Army::Rest() 
{
    m_pState->Rest();
}

void Army::Fiesta() 
{
    m_pState->Fiesta();
}

其实也没什么了。很容易的理解的。不知道汉武时代有没有过阅兵仪式,如果有,那就会又多一个状态,想想我们该如何解决?挺简单了,为State添加一个新的子类,并为它提供一个阅兵的启动方法,当然相应的子类也需要添加。相应的Army类中也需要添加该方法。这样做,我们只是扩展了原有类的方法,而不会去改动它原有的功能。这样就可以避免给原有功能带来bug了。

再看看该类的调用:

int main(int argc, char* argv[]) 
{  
    Army army;   
    army.Advance(); 
    army.Assault(); 
    army.Rest();  
    army.Fiesta(); 
    army.Assault();  
    army.Assault();  
    army.Rest();  
    army.Fiesta();  
    system("pause"); 
    return 0; 
}

创建对象后,我们可以直接调用它的函数来实现状态的转换了。
     好了,状态模式,我们先讲到这里了。回想一下上回的策略模式,是不是觉得很象?在《Head First Design Model》中,该模式开篇的扉页上画的是一幅煽情的母亲流泪的图片,说她眼瞅着自己的孩子分离,此处的两个孩子就是策略模式和状态模式。我们可以这两个模式的原理都是将一个类中的属性提炼成一个接口,再通过该接口实现对其子类的调用而完成所属类的功能。它们的不同是状态模式的接口的变化是在接口子类中完成的,而策略模式是通过所属类来完成的。差别只是这一点,具体工作中使用哪个模式,那就的具体问题具体分析了。你只需记住我们红体标记的规则,就可以以不变应万变了。

class AdvanceState : public State
{
private: 
    Army *m_pArmy;
public: 
    AdvanceState(Army *pArmy); 
    virtual void Advance(); 
    virtual void Assault();
    virtual void Rest() ; 
    virtual void Fiesta();
}; 
我们再来看一下AdvanceState的具体实现:
AdvanceState::AdvanceState(Army *pArmy):m_pArmy(pArmy)
{
}
void AdvanceState::Advance()
{
    cout << "Be in Advancing!" << endl;
}
void AdvanceState::Assault()
{
    //设置假想的敌人数
    m_pArmy->SetEmptyCount(200);
    cout << "Ok!Assault!" << endl;
    m_pArmy->SetState(m_pArmy->GetAssaultState());
}
void AdvanceState::Rest()
{
    cout << "OK!Rest!" << endl;
    m_pArmy->SetState(m_pArmy->GetRestState());
}
void AdvanceState::Fiesta()
{
    cout << "sorry!can't Fiesta!" << endl;
}
其实也没什么了。很容易的理解的。不知道汉武时代有没有过阅兵仪式,如果有,那就会又多一个状态,想想我们该如何解决?挺简单了,为State添加一个新的子类,并为它提供一个阅兵的启动方法,当然相应的子类也需要添加。相应的Army类中也需要添加该方法。这样做,我们只是扩展了原有类的方法,而不会去改动它原有的功能。这样就可以避免给原有功能带来bug了。

再看看该类的调用:

int main(int argc, char* argv[]) 
{  
    Army army;   
    army.Advance(); 
    army.Assault(); 
    army.Rest();  
    army.Fiesta(); 
    army.Assault();  
    army.Assault();  
    army.Rest();  
    army.Fiesta();  
    system("pause"); 
    return 0; 
}

创建对象后,我们可以直接调用它的函数来实现状态的转换了。
     好了,状态模式,我们先讲到这里了。回想一下上回的策略模式,是不是觉得很象?在《Head First Design Model》中,该模式开篇的扉页上画的是一幅煽情的母亲流泪的图片,说她眼瞅着自己的孩子分离,此处的两个孩子就是策略模式和状态模式。我们可以这两个模式的原理都是将一个类中的属性提炼成一个接口,再通过该接口实现对其子类的调用而完成所属类的功能。它们的不同是状态模式的接口的变化是在接口子类中完成的,而策略模式是通过所属类来完成的。差别只是这一点,具体工作中使用哪个模式,那就的具体问题具体分析了。你只需记住我们红体标记的规则,就可以以不变应万变了。

乱砍设计模式之二 -- STATE模式相关推荐

  1. 乱砍设计模式之一 -- STRATEGY 模式

    转自 : http://www.vckbase.com/document/viewdoc/?id=1633 STRATEGY在中文中被译成了策略,我感觉这个意思并不妥切,但翻英文词典能得到的翻译也只有 ...

  2. 乱砍设计模式之一 -- STRATEGY模式

    STRATEGY模式---赵子龙单骑救主 junguo STRATEGY在中文中被译成了策略,我感觉这个意思并不妥切,但翻英文词典能得到的翻译也只有这个,我的词典比较简单,不知道是否还有其它意思?如果 ...

  3. java状态机设计模式_设计模式总结-State模式

    不同的状态,不同的行为;或者说,每个状态有着相应的行为. 二.State模式的适用场合: State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif ...

  4. 虚无空间java下载_Java进阶篇设计模式之二 ----- 工厂模式

    前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...

  5. 乱砍设计模式之三 -- DECORATOR模式

    DECORATOR中文的意思是装饰,该模式的动机是帮助对象动态的添加一些功能.它强调是为对象而不是为类添加功能.为类添加功能最有效的方式是通过继承来实现,但继承的缺点是不够灵活.下面我们还是通过例子来 ...

  6. 一天一种设计模式之二-----备忘录模式

    2019独角兽企业重金招聘Python工程师标准>>> 一.备忘录模式 备忘录模式属于三种设计模式中的行为型模式(另外两种是创建型模式和结构型模式). 定义:在不破坏封闭性的前提下, ...

  7. C++设计模式之二 AbstractFactory模式

    设计模式的目的就是尽量减少"变化"对程序的影响,尤其是对客户程序的影响.AbstractFactory模式作为创建型模式的一种,解决的就是"new"在变化中可能 ...

  8. 设计模式之二——工厂模式

    1.工厂模式简介 1.1定义 简单工厂模式(Simple Factory Pattern):专门定义一个类(工厂类)来负责创建其他类的实例.可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都 ...

  9. 设计模式之二-Proxy模式

    代理模式 组成: 抽象角色:通过接口或抽象类声明真实角色实现的业务方法. 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作. 真实角色:实现抽象 ...

最新文章

  1. 【权游相关】龙的简史 | 混乱博物馆
  2. linux下1sa 密码失效,SA口令
  3. 小白学数据分析-----聚类分析理论之K-means理论篇
  4. 双向链表的(CRUD)
  5. 基于Spring Boot Profile的日志记录示例样本
  6. LeetCode 1078. Bigram 分词
  7. Elasticsearch是如何实现master选举的?
  8. Java 并发编程ReentrantLock--源码解析(底层AQS实现)
  9. Linux防止暴力破解密码脚本
  10. 实现透明背景但背景上元素不透明
  11. Android开发之局域网聊天软件
  12. 我的实用小软件(持续更新)
  13. 计算机用户禁用更改登记,注册表被禁用的解法.doc
  14. php time()的用法,PHP timezone_name_from_abbr() 函数用法及示例
  15. 和风天气开发平台使用
  16. 2.Deep Crossing: Web-Scale Modeling without Manually Crafted Combinatorial Features论文核心解读以及代码实现
  17. 图形商标近似检索-知擎者的 Milvus 实践
  18. 汇丰,用数字技术创新奇妙体验
  19. 发生了Post错误:错误代码40005,微信返回错误信息:invalid file type
  20. Python模块:Re模块、附软件开发目录规范

热门文章

  1. Vue3.0源码解读 - 响应式系统
  2. 4GL是什么?计算机辅助软件工程CASE是什么?
  3. 网络推广100法,果然很强悍。
  4. android系统应用之Settings
  5. java开发人员macOS系统上需要安装软件以及常用快捷键
  6. 10分钟读懂技术分析经典—《日本蜡烛图技术》
  7. 操作系统OS第一章练习作业【附答案详解】
  8. 静态方法:关于Java8中的日期时间API,你需要掌握这些!!
  9. 朴素贝叶斯(演示结果与SVM进行对比)
  10. 指尖江湖李忘生鸿蒙初开,剑网3指尖江湖李忘生怎么样_李忘生装备搭配、技能特性、解锁方法介绍_游戏吧...