“狀態(tài)變化”模式
在組建構(gòu)建過程中,某些對象的狀態(tài)經(jīng)常面臨變化,如何對這些變化進(jìn)行有效的管理?同時(shí)又維持高層模塊的穩(wěn)定苍凛?“狀態(tài)變化”模式為這一個(gè)問題提供了一種解決方案。
- 典型模式
- State
- Memento
狀態(tài)模式(State)
允許一個(gè)對象在其內(nèi)部狀態(tài)改變是改變它的行為兵志。從而使對像看起來似乎修改其行為醇蝴。
——《設(shè)計(jì)模式》GoF
- 動機(jī)
在軟件構(gòu)建過程中,某些對象的狀態(tài)如果改變想罕,其行為也會隨之而發(fā)生變化悠栓,比如文檔處于只讀狀態(tài),其支持的行為和讀寫狀態(tài)支持的行為就可能會完全不同。
假設(shè)有一個(gè)網(wǎng)絡(luò)的應(yīng)用惭适,移動有三種狀態(tài)笙瑟,打開、關(guān)閉和連接癞志。
操作針對不同的狀態(tài)會執(zhí)行不同的行為往枷。(狀態(tài)不同,行為不同)
同時(shí)執(zhí)行一次操作完畢后凄杯,也會更改狀態(tài)师溅。
enum NetworkState
{
Network_Open,
Network_Close,
Network_Connect,
};
class NetworkProcessor{
NetworkState state;
public:
void Operation1(){
if (state == Network_Open){
//**********
state = Network_Close;
}
else if (state == Network_Close){
//..........
state = Network_Connect;
}
else if (state == Network_Connect){
//$$$$$$$$$$
state = Network_Open;
}
}
public void Operation2(){
if (state == Network_Open){
//**********
state = Network_Connect;
}
else if (state == Network_Close){
//.....
state = Network_Open;
}
else if (state == Network_Connect){
//$$$$$$$$$$
state = Network_Close;
}
}
public void Operation3(){
}
};
對于上面的代碼來說,未來狀態(tài)是否會發(fā)生變化盾舌?如果出現(xiàn)了新的變化,那么之后的所有的代碼蘸鲸,全部都需要改變妖谴,違背了開閉原則。
那么根據(jù)Strategy模式的啟發(fā)酌摇,我們把狀態(tài)的向上抽象膝舅,每個(gè)自己實(shí)現(xiàn)出來的狀態(tài),將所有跟狀態(tài)有關(guān)的操作全都變成具體的狀態(tài)對像的行為窑多。每個(gè)狀態(tài)也可以次啊用單例的模式仍稀。每個(gè)狀態(tài)的操作之后,可以根據(jù)他的操作完后埂息,在操作的方法中技潘,將自動定義下一個(gè)狀態(tài)。對于調(diào)用者來說千康,每次的狀態(tài)改變只需要將指針指向操作所改變的下一個(gè)對象即可享幽,那么使用者完全不需要考慮下一個(gè)狀態(tài)是什么,因?yàn)樵谥贫顟B(tài)的操作中已經(jīng)改變好了拾弃。
class NetworkState{
public:
NetworkState* pNext;
virtual void Operation1()=0;
virtual void Operation2()=0;
virtual void Operation3()=0;
virtual ~NetworkState(){}
};
class OpenState :public NetworkState{
static NetworkState* m_instance;
public:
static NetworkState* getInstance(){
if (m_instance == nullptr) {
m_instance = new OpenState();
}
return m_instance;
}
void Operation1(){
//**********
pNext = CloseState::getInstance();
}
void Operation2(){
//..........
pNext = ConnectState::getInstance();
}
void Operation3(){
//$$$$$$$$$$
pNext = OpenState::getInstance();
}
};
class CloseState:public NetworkState{ }
//...
class NetworkProcessor{
NetworkState* pState;
public:
NetworkProcessor(NetworkState* pState){
this->pState = pState;
}
void Operation1(){
//...
pState->Operation1();
pState = pState->pNext;
//...
}
void Operation2(){
//...
pState->Operation2();
pState = pState->pNext;
//...
}
void Operation3(){
//...
pState->Operation3();
pState = pState->pNext;
//...
}
};
要點(diǎn)總結(jié)
- State模式將所有與一個(gè)特定狀態(tài)相關(guān)的行為都放入一個(gè)State的子類對象中值桩,在對像狀態(tài)切換時(shí), 切換相應(yīng)的對象豪椿;但同時(shí)維持State的接口奔坟,這樣實(shí)現(xiàn)了具體操作與狀態(tài)轉(zhuǎn)換之間的解耦。
- 為不同的狀態(tài)引入不同的對象使得狀態(tài)轉(zhuǎn)換變得更加明確搭盾,而且可以保證不會出現(xiàn)狀態(tài)不一致的情況咳秉,因?yàn)檗D(zhuǎn)換是原子性的——即要么徹底轉(zhuǎn)換過來,要么不轉(zhuǎn)換鸯隅。
- 如果State對象沒有實(shí)例變量滴某,那么各個(gè)上下文可以共享同一個(gè)State對象,從而節(jié)省對象開銷。
備忘錄(Memento)
在不破壞封裝性的前提下霎奢,不活一個(gè)對象的內(nèi)部狀態(tài)户誓,并在該對像之外保存這個(gè)狀態(tài)。這樣以后就可以將該對像恢復(fù)到原想保存的狀態(tài)幕侠。
——《設(shè)計(jì)模式》GoF
- 動機(jī)
在軟件構(gòu)建過程中帝美,某些對象的狀態(tài)在轉(zhuǎn)會過程中,可能由于某種需求晤硕,要求程序能夠回溯到對像之前處于某個(gè)點(diǎn)時(shí)的狀態(tài)悼潭。如果使用一些公有借口來讓其它對象得到對象的狀態(tài),便會暴露對象的實(shí)現(xiàn)細(xì)節(jié)舞箍。
class Memento
{
string state;
//..
public:
Memento(const string & s) : state(s) {}
string getState() const { return state; }
void setState(const string & s) { state = s; }
};
class Originator
{
string state;
//....
public:
Originator() {}
Memento createMomento() {
Memento m(state);
return m;
}
void setMomento(const Memento & m) {
state = m.getState();
}
};
int main()
{
Originator orginator;
//捕獲對象狀態(tài)舰褪,存儲到備忘錄
Memento mem = orginator.createMomento();
//... 改變orginator狀態(tài)
//從備忘錄中恢復(fù)
orginator.setMomento(memento);
}
假設(shè)Originator是一個(gè)需要被保存的對象,他的內(nèi)部可能有很多的狀態(tài)疏橄,我們有一個(gè)需求:在某一個(gè)時(shí)間節(jié)點(diǎn)占拍,為這個(gè)對象拍一個(gè)快照。那么我們設(shè)計(jì)了一個(gè)Memento對象捎迫,其中具有和Originator需要備份的屬性晃酒。Memento具有供外界調(diào)用的接口。那么被創(chuàng)建的備份窄绒,應(yīng)該是穩(wěn)定的贝次,外界只有讀取信息的結(jié)構(gòu),沒有修改信息的接口彰导。
在Orginator還應(yīng)該具有從Memento恢復(fù)的方法蛔翅,來依靠之前所存儲的快照進(jìn)行恢復(fù)。
以上僅僅是一個(gè)示意性的偽碼描述位谋,也可以通過其他的手段來實(shí)現(xiàn)Memento的過程搁宾,比如對象的序列化和反序列化等等。
要點(diǎn)總結(jié)
備忘錄(Memento)存儲原發(fā)器(Originator)對象的內(nèi)部狀態(tài)倔幼,在需要時(shí)恢復(fù)原發(fā)器的狀態(tài)盖腿。
Memento模式的核心是信息隱藏,即Originator需要向外接隱藏信息损同,保持其封裝性翩腐。但同時(shí)又需要將其狀態(tài)保持到外界(Memento)
由于現(xiàn)代語言運(yùn)行時(shí)(如C#、java等)都具有相當(dāng)?shù)膶ο笮蛄谢С指嗳迹虼送捎眯瘦^高茂卦、有較容易正確實(shí)現(xiàn)的序列化方案來實(shí)現(xiàn)Memento模式。
由于《設(shè)計(jì)模式》是在94年定義的组哩,現(xiàn)在很多的技術(shù)發(fā)展已經(jīng)變化等龙,現(xiàn)在Memento的實(shí)現(xiàn)方法已經(jīng)過時(shí)了处渣,現(xiàn)在在使用其他的方式在做這件事,但是對于思想并沒有發(fā)生變化蛛砰。在備份的過程中罐栈,還需要保持封裝的時(shí)候,將信息保存到外部泥畅。