詳解state狀態(tài)模式及在C++設計模式編程中的使用實例
每個人、事物在不同的狀態(tài)下會有不同表現(動作),而一個狀態(tài)又會在不同的表現下轉移到下一個不同的狀態(tài)(State)。最簡單的一個生活中的例子就是:地鐵入口處,如果你放入正確的地鐵票,門就會打開讓你通過。在出口處也是驗票,如果正確你就可以 ok,否則就不讓你通過(如果你動作野蠻,或許會有報警(Alarm),:))。
有限狀態(tài)自動機(FSM)也是一個典型的狀態(tài)不同,對輸入有不同的響應(狀態(tài)轉移)。
通常我們在實現這類系統(tǒng)會使用到很多的 Switch/Case 語句,Case 某種狀態(tài),發(fā)生什么動作,Case 另外一種狀態(tài),則發(fā)生另外一種狀態(tài)。但是這種實現方式至少有以下兩個問題:
當狀態(tài)數目不是很多的時候,Switch/Case 可能可以搞定。但是當狀態(tài)數目很多的時候(實際系統(tǒng)中也正是如此),維護一大組的 Switch/Case 語句將是一件異常困難并且容易出錯的事情。
狀態(tài)邏輯和動作實現沒有分離。在很多的系統(tǒng)實現中,動作的實現代碼直接寫在狀態(tài)的邏輯當中。這帶來的后果就是系統(tǒng)的擴展性和維護得不到保證。
狀態(tài)模式就是被用來解決上面列出的兩個問題的,在狀態(tài)模式中我們將狀態(tài)邏輯和動作實現進行分離。當一個操作中要維護大量的 case 分支語句,并且這些分支依賴于對象的狀態(tài)。狀態(tài)模式將每一個分支都封裝到獨立的類中。
狀態(tài)模式典型的結構圖為:
狀態(tài)模式的實現
代碼片斷 1:State.h
//state.h #ifndef _STATE_H_ #define _STATE_H_ class Context; //前置聲明 class State{ public: State(); virtual ~State(); virtual void OperationInterface(Context* ) = 0; virtual void OperationChangeState(Context*) = 0; protected: bool ChangeState(Context* con,State* st); private: //bool ChangeState(Context* con,State* st); }; class ConcreteStateA:public State{ public: ConcreteStateA(); virtual ~ConcreteStateA(); virtual void OperationInterface(Context* ); virtual void OperationChangeState(Context*); protected: private: }; class ConcreteStateB:public State{ public: ConcreteStateB(); virtual ~ConcreteStateB(); virtual void OperationInterface(Context* ); virtual void OperationChangeState(Context*); protected: private: }; #endif //~_STATE_H_
代碼片斷 2:State.cpp
//State.cpp #include "State.h" #include "Context.h" #include <iostream> using namespace std; State::State(){ } State::~State(){ } void State::OperationInterface(Context* con){ cout<<"State::.."<<endl; } bool State::ChangeState(Context* con,State* st){ con->ChangeState(st); return true; } void State::OperationChangeState(Context* con){ } /// ConcreteStateA::ConcreteStateA(){ } ConcreteStateA::~ConcreteStateA(){ } void ConcreteStateA::OperationInterface(Context* con){ cout<<"ConcreteStateA::OperationInterface ......"<<endl; } void ConcreteStateA::OperationChangeState(Context* con){ OperationInterface(con); this->ChangeState(con,new ConcreteStateB()); } /// ConcreteStateB::ConcreteStateB(){ } ConcreteStateB::~ConcreteStateB(){ } void ConcreteStateB::OperationInterface(Context* con){ cout<<"ConcreteStateB::OperationInterface......"<<endl; } void ConcreteStateB::OperationChangeState(Context* con){ OperationInterface(con); this->ChangeState(con,new ConcreteStateA()); }
代碼片斷 3:Context.h
//context.h #ifndef _CONTEXT_H_ #define _CONTEXT_H_ class State; /** * **/ class Context{ public: Context(); Context(State* state); ~Context(); void OprationInterface(); void OperationChangState(); protected: private: friend class State; //表明在 State 類中可以訪問 Context 類的 private 字段 bool ChangeState(State* state); private: State* _state; }; #endif //~_CONTEXT_H_
代碼片斷 4:Context.cpp
//context.cpp #include "Context.h" #include "State.h" Context::Context(){ } Context::Context(State* state){ this->_state = state; } Context::~Context(){ delete _state; } void Context::OprationInterface(){ _state->OperationInterface(this); } bool Context::ChangeState(State* state){ ///_state->ChangeState(this,state); this->_state = state; return true; } void Context::OperationChangState(){ _state->OperationChangeState(this); }
代碼片斷 5:main.cpp
//main.cpp #include "Context.h" #include "State.h" #include <iostream> using namespace std; int main(int argc,char* argv[]){ State* st = new ConcreteStateA(); Context* con = new Context(st); con->OperationChangState(); con->OperationChangState(); con->OperationChangState(); if (con != NULL) delete con; if (st != NULL) st = NULL; return 0; }
代碼說明:狀態(tài)模式在實現中,有兩個關鍵點:
1.將狀態(tài)聲明為 Context 的友元類(friend class),其作用是讓狀態(tài)模式訪問 Context的 protected 接口 ChangeSate()。
狀態(tài)及其子類中的操作都將 Context*傳入作為參數,其主要目的是狀態(tài)類可以通過這個指針調用 Context 中的方法(在本示例代碼中沒有體現)。這也是狀態(tài)模式和 Strategy模式的最大區(qū)別所在。
2.運行了示例代碼后可以獲得以下的結果:連續(xù) 3 次調用了 Context 的 OprationInterface()因為每次調用后狀態(tài)都會改變(A-B-A),因此該動作隨著 Context 的狀態(tài)的轉變而獲得了不同的結果。
關于State模式的一些需要注意的地方
這個模式使得軟件可以在不同的state下面呈現出完全不同的特征
不同的theme使得相同的元素呈現出不同的特點
不同的state下面相同的操作產生不同的效果
不同的狀態(tài)對相同的信息產生不同的處理
這個模式使得操作的state邏輯更加的清楚,省去了無數的state判斷,而state的擴展性和可維護性和執(zhí)行效率也大幅度的上升。關于state,有如下幾點要注意的地方:
1.所有的state應該被一個類(State Manager Class)管理:
state之間的跳轉和轉換是非常復雜的,有時一些state可能要跳轉的目標state有幾十個,這個時候我們需要一個管理類(State Manager )來統(tǒng)一的管理這些state的切換,例如目標state的初始化和申請?zhí)Dstate的結束處理,以及一些state間共享數據的存儲和處理。與其稱這個Manager 為管理類,不如說是一個中間類,它實現了state之間的解隅,使得各個state之間不比知道target state的具體信息,而只要向Manager申請?zhí)D就可以了。使得各個state的模塊化更好,更加的靈活
2.所有的state都應該從一個state基類繼承:
既然state要教給一個manager來管理,那么自然的,這些state都應該從一個父類繼承下來,這樣manager并不需要知道很多子類的信息,一個最單純的manager只要只要管理一個這樣的基類的指針就可以了。另外,我們還可以統(tǒng)一的把state的一些共有的屬性放在這里
3.state應該實現為一個singleton:
state并不需要總是被申請,這樣可能會造成管理上的混亂,state資源的申請也不應該可以任意進行,事實上,state的申請權限應該只有 Manager才有,并且有且只有一次。在這樣的情況下,state的構造函數似乎應該被聲明為protected or private ,而Manager應該被聲明為state的友元,但是友元被看成是破壞類的封裝性的一種做法,這一點上,我很矛盾,所以在這一條上我只能采取一種漠視的態(tài)度。
4.應該做一個state么?這是一個問題:
state可以說是if-else的一種替代品,極端的情況下面state可以讓你的程序中if-else程序塊消失得無影無蹤,但是,這并不是銀彈。state對于狀態(tài)可預知的情況下非常有效,但是對于state不可預知,或者相似的state數量太多。過多的state會造成class的粒度過細,程序反而不簡潔。在這樣的情況下,你應該考慮使用if-else程序塊來替代state。
例如:
有這樣的一個程序,它可以生成任意形狀的多邊形,而多邊形的各個節(jié)點是可以移動的,問題就來了。
我并不知道用戶將要使用多少個節(jié)點的多邊形,因此我無法的創(chuàng)建那么多相應的state來使得這樣一個程序正常工作。state大多數都是確定的,對于不確定的,state似乎無能為力,例如此例
一種解決方法是我利用Manager傳遞給state一個state參數,讓state有機會知道用戶的操作意圖,在這個例子里面是讓state知道用戶打算操作某一個節(jié)點,而state根據這個state參數來處理用戶的操作,比如說,state得到的是用戶操作的某一個點的index ,而state只要寫
points[index].moveTo(points[index].getX()+offset_x , points[index].getY()+offset_y);
就可以,從而避免了state過多出現的問題。
欄 目:C語言
下一篇:C++設計模式編程中Facade外觀模式的使用實例解析
本文標題:詳解state狀態(tài)模式及在C++設計模式編程中的使用實例
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2459.html
您可能感興趣的文章
- 01-10求子數組最大和的解決方法詳解
- 01-10深入二叉樹兩個結點的最低共同父結點的詳解
- 01-10數據結構課程設計- 解析最少換車次數的問題詳解
- 01-10數據結構課程設計-用棧實現表達式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應用分析詳解
- 01-10使用C++實現全排列算法的方法詳解
- 01-10如何查看進程實際的內存占用情況詳解
- 01-10深入Main函數中的參數argc,argv的使用詳解
- 01-10APUE筆記之:進程環(huán)境詳解
- 01-10深入第K大數問題以及算法概要的詳解


閱讀排行
本欄相關
- 04-02c語言函數調用后清空內存 c語言調用
- 04-02func函數+在C語言 func函數在c語言中
- 04-02c語言的正則匹配函數 c語言正則表達
- 04-02c語言用函數寫分段 用c語言表示分段
- 04-02c語言中對數函數的表達式 c語言中對
- 04-02c語言編寫函數冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數 round c語言
- 04-02c語言分段函數怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數 c語言中怎
- 04-02c語言調用函數求fibo C語言調用函數求
隨機閱讀
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實例總結
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05dedecms(織夢)副欄目數量限制代碼修改
- 01-11ajax實現頁面的局部加載
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢data目錄下的sessions文件夾有什