(Boolan)C++設(shè)計(jì)模式 <十> ——狀態(tài)模式(State)和備忘錄(Memento)

“狀態(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;
        //...
    }

};
State的UML

要點(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的過程搁宾,比如對象的序列化和反序列化等等。

Memento的UML

要點(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í)候,將信息保存到外部泥畅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荠诬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子位仁,更是在濱河造成了極大的恐慌柑贞,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聂抢,死亡現(xiàn)場離奇詭異钧嘶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)琳疏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門有决,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人轿亮,你說我怎么就攤上這事⌒厍剑” “怎么了我注?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長迟隅。 經(jīng)常有香客問我但骨,道長,這世上最難降的妖魔是什么智袭? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上拔稳,老公的妹妹穿的比我還像新娘诡宗。我一直安慰自己,他們只是感情好瞳步,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布闷哆。 她就那樣靜靜地躺著,像睡著了一般单起。 火紅的嫁衣襯著肌膚如雪抱怔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天嘀倒,我揣著相機(jī)與錄音屈留,去河邊找鬼局冰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛灌危,可吹牛的內(nèi)容都是我干的康二。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼乍狐,長吁一口氣:“原來是場噩夢啊……” “哼赠摇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浅蚪,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤藕帜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惜傲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洽故,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年盗誊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了时甚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哈踱,死狀恐怖荒适,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情开镣,我是刑警寧澤刀诬,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站邪财,受9級特大地震影響陕壹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜树埠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一糠馆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怎憋,春花似錦又碌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至愤炸,卻和暖如春期揪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背规个。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工凤薛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留姓建,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓缤苫,卻偏偏與公主長得像速兔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子活玲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容