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


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實(shí)例總結(jié)
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 01-10delphi制作wav文件的方法
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什


