源代码下载 严风吹霜海草凋,筋干精坚胡马骄。 汉家战士三十万,将军兼领霍嫖姚。 流星白羽腰间插,剑花秋莲光出匣。 天兵照雪下玉关,虏箭如沙射金甲。 云龙风虎尽交回,太白入月敌可摧。 敌可摧,旄头灭,履胡之肠涉胡血。 悬胡青天上,埋胡紫塞旁。 胡无人,汉道昌。 李白这首诗豪气万丈的诗篇,描述的是汉骠骑将军霍去病率领大军,出陇西,突袭匈奴部落的战争场面,千载之下,犹让人感觉豪迈异常。匈奴和汉朝的争端从汉高祖刘邦开始。楚汉之争过后,刘邦统一了中国。此时,匈奴部族也被冒顿(这两字发音应为mo du)单于统一了起来。匈奴逐渐强大的同时,开始窥觑汉朝的疆土。冒顿单于领军进犯太原,包围晋阳。刘邦亲率30万大军进击匈奴,不期竟中匈奴之计,被困于白登。幸亏用陈平之计,送重礼于冒顿夫人,走枕边风路线,才让匈奴大军让开一条路,逃了出来,狼狈之极。这场战役的结果就是汉朝采取和亲政策,送汉宗室之女给匈奴王为妾,并借机进贡。但这也避免不了匈奴侵犯汉族边界,杀戮汉民,抢夺牲畜。这样的历史持续了数十年,直到汉武帝的时候还保持着。而雄才大略的武帝,岂能容忍此奇耻大辱。坚毅勇猛的他,筹划着对匈奴的打击,经过一系列的改革(个人觉得他改革兵种,将以车兵步兵为主的军队改为轻骑兵为主,对这场战役意义最为重大),开始了对匈奴的穷追猛打。这段历史造就了中国历史上两颗耀眼的将星:卫青,霍去病。霍去病更加夺目,他18岁的时候,随卫青大军出征。他率领800轻骑,远离大军,奔赴敌人腹地,斩敌二千余人。被汉武帝封为冠军侯。后又率1万大军出陇西,转战千余里,斩获8000于人。再于同年秋天,出北地,攻祁连山,大获全胜,斩获三万余人。汉朝与匈奴的最后一次大型战役也由卫青,霍去病完成。霍去病亲率5万骑兵远离代郡,对匈奴人穷追猛打,歼敌七万余人。乘胜追击到狼居胥山(今在蒙古境内),并在此举行祭天仪式(封狼居胥)。此时的霍去病仅仅21岁,但二年后英年早逝。在他短暂的一生中四次与匈奴做战,转战数千余里,灭敌十余万人,彻底熄灭了匈奴人的嚣张气焰,也给大汉的边境带来了安宁。不仅如此,他还留下了“匈奴未灭,何以家为”的千古豪言。真是铮铮男儿形象,为万世铁骨男儿楷模。 #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; } else 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; } else 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; } else 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对象的指针,是因为需要在内部修改状态,具体的代码中可以看到。 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; }很简单了,就是根据当前状态来处理各个函数。我们看到有这样的函数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》中,该模式开篇的扉页上画的是一幅煽情的母亲流泪的图片,说她眼瞅着自己的孩子分离,此处的两个孩子就是策略模式和状态模式。我们可以这两个模式的原理都是将一个类中的属性提炼成一个接口,再通过该接口实现对其子类的调用而完成所属类的功能。它们的不同是状态模式的接口的变化是在接口子类中完成的,而策略模式是通过所属类来完成的。差别只是这一点,具体工作中使用哪个模式,那就的具体问题具体分析了。你只需记住面向对象设计的规则,就可以以不变应万变了。
参考书目
|
|
来自: kyo_siye > 《乱砍设计模式系列》