設(shè)計模式之備忘錄模式.

本文是對設(shè)計模式之禪一書中備忘錄模式一章的總結(jié)與整理迂求。

1. 定義

在不破壞封裝性的前提下惶我,捕獲一個對象的內(nèi)部狀態(tài)专挪,并在該對象之外保存這個狀態(tài)接箫,這樣以后就可以將對象恢復(fù)到原先保存的的狀態(tài)攒读,我就不明月了,為啥這些大師總喜歡將定義寫的這么復(fù)雜呢辛友,我看極有可能是自己的水平?jīng)]那個份上薄扁,這個定義寫成接地氣的話就是“在操作一個對象前,把這個對象臨時保存一份废累,以便將來恢復(fù)邓梅,相當(dāng)于是后悔藥∫乇酰”通用類圖如下:

通用類圖.png

  1. Originator 發(fā)起人角色:記錄當(dāng)前時刻的內(nèi)部狀態(tài)日缨,負(fù)責(zé)定義哪些屬于備份的狀態(tài),負(fù)責(zé)創(chuàng)建和恢復(fù)備忘錄數(shù)據(jù)掖看。
  1. Memento 備忘錄角色:負(fù)責(zé)存儲Originator發(fā)起人對象的內(nèi)部狀態(tài) 匣距,在需要的時候提供發(fā)起人需要的內(nèi)部狀態(tài)
  1. Caretaker備忘錄管理員角色:對備忘錄進(jìn)行管理、保存和提供備忘錄

書中說這類標(biāo)準(zhǔn)的模很難在項目中遇到哎壳,基本者是變換后的處理方式毅待,所以此處不再提供示例。

2. 擴(kuò)展

第一種:Clone方式的備忘錄

通過復(fù)制的方式產(chǎn)生一個對象的內(nèi)部狀態(tài)归榕,類圖如下:

類圖1.png

發(fā)起人:


public class Originator implements Cloneable {
    private Originator backup;
    //內(nèi)部狀態(tài)
    private String state="";
    public String getState(){
        return this.state;
    }
    public void setState(String _state){
        this.state=_state;
    }

    //創(chuàng)建備忘錄
    public void createMemento(){
        this.backup =  this.clone();
    }

    //克隆當(dāng)前對象
    @Override
    protected Originator clone(){
        try {
            return (Originator)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }

    //恢復(fù)備忘錄
    public void restoreMemento(){
        this.setState(this.backup.getState());
    }
}

備忘錄管理者


public class Caretaker {
    //發(fā)起人對象
    private Originator originator;

    public Originator getOriginator(){
        return  this.originator;
    }

    public void setOriginator(Originator _originator){
        this.originator=_originator;
    }


}

場景類:


public class Client {
    public static void main(String[] strings) {
        Originator originator = new Originator();
        //初始狀態(tài)
        originator.setState("初始狀態(tài).....");
        System.out.println("初始狀態(tài)是:"+originator.getState());
        //備份
        originator.createMemento();
        //修改狀態(tài)
        originator.setState("修改狀態(tài)....");
        System.out.println("修改后的狀態(tài)為:"+originator.getState());
        //恢復(fù)狀態(tài)
        originator.restoreMemento();
        System.out.println("恢復(fù)后的狀態(tài)為:"+originator.getState());
    }
}

運行結(jié)果如下:

結(jié)果1.png
第二類:多狀態(tài)的備忘錄模式

第一種情況中只一種狀態(tài)尸红,如果有多種狀態(tài)就采用這種方式,通用類圖如下:

類圖2.png

再來看代碼,發(fā)起人:


public class Originator implements Cloneable {

    //內(nèi)部狀態(tài)
    private String state1="";

    public String getState2() {
        return state2;
    }

    public void setState2(String state2) {
        this.state2 = state2;
    }

    public String getState1() {
        return state1;
    }

    public void setState1(String state1) {
        this.state1 = state1;
    }

    public String getState3() {
        return state3;
    }

    public void setState3(String state3) {
        this.state3 = state3;
    }

    private String state2="";
    private String state3="";



    //創(chuàng)建備忘錄
    public Memento createMemento(){
        return new Memento(BeanUtils.backupProps(this));
    }
    //恢復(fù)備忘錄
    public void restoreMemento(Memento _memento){
       BeanUtils.restoreProps(this,_memento.getStateMap());
    }

    //toString


    @Override
    public String toString() {
        return "state1="+state1+",state2="+state2+",state3="+state3;
    }
}

備忘錄如下:


public class Memento {
    public HashMap<String, Object> getStateMap() {
        return stateMap;
    }

    public void setStateMap(HashMap<String, Object> stateMap) {
        this.stateMap = stateMap;
    }

    //接愛HashMap作為狀態(tài)
    private HashMap<String,Object> stateMap;
    //接受一個對象,建立備份
    public Memento(HashMap<String,Object> map){
        this.stateMap=map;
    }

}

工具類:


public class BeanUtils {
    public static HashMap<String,Object> backupProps(Object bean){
        HashMap<String,Object> result=new HashMap<>();
        try {
            //獲取屬性描述
            BeanInfo beanInfo= Introspector.getBeanInfo(bean.getClass());
            //獲得屬性
            PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();
            //遍歷
            for(PropertyDescriptor des:descriptors){
                String fieldName=des.getName();
                Method getter=des.getReadMethod();
                Object fieldValue=getter.invoke(bean,new Object[]{});
                if(!fieldName.equalsIgnoreCase("class")){
                    result.put(fieldName,fieldValue);
                }
            }
        }catch (Exception ex){

        }
        return  result;
    }

    public static void restoreProps(Object bean,HashMap<String,Object> propMap){
        //獲取屬性描述
        BeanInfo beanInfo= null;
        try {
            beanInfo = Introspector.getBeanInfo(bean.getClass());
            //獲得屬性
            PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();
            for(PropertyDescriptor des:descriptors){
                String fieldName=des.getName();
                if(propMap.containsKey(fieldName)){
                    Method setter=des.getWriteMethod();
                    setter.invoke(bean,new Object[]{propMap.get(fieldName)});
                }
            }
        } catch (Exception e) {
            System.out.println("shit");
            e.printStackTrace();
        }

    }
}

備忘錄管理員角色:


public class Caretaker {
    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }

    private Memento memento;



}

場景類:


public class Client {
    public static void main(String[] strings){
       Originator originator=new Originator();

        originator.setState1("中國");
        originator.setState2("強(qiáng)盛");
        originator.setState3("繁榮");
        System.out.println("初始狀態(tài):"+originator.toString());
        //創(chuàng)建一個備忘錄
        Caretaker caretaker=new Caretaker();
        caretaker.setMemento(originator.createMemento());
        originator.setState1("軟件");
        originator.setState3("架構(gòu)");
        originator.setState2("優(yōu)秀");
        System.out.println("后狀態(tài):"+originator.toString());

        originator.restoreMemento(caretaker.getMemento());

        System.out.println("原狀態(tài):"+originator.toString());
    }
}

運行結(jié)果如下:

結(jié)果2.png
第三種情況是多備份外里,就是有多個備份的情況怎爵,再看備忘錄管理員角色如下:

public class Caretaker {

    //創(chuàng)建一個容器
    private HashMap<String, Memento> mementoHashMap = new HashMap<>();

    public Memento getMemento(String idx) {
        return mementoHashMap.get(idx);
    }

    public void setMementoHashMap(String idx, Memento memento) {
        this.mementoHashMap.put(idx, memento);
    }
}
``

再來看場景類如下:

```java

public class Client {
    public static void main(String[] strings){
       Originator originator=new Originator();

        originator.setState1("中國");
        originator.setState2("強(qiáng)盛");
        originator.setState3("繁榮");
        System.out.println("初始狀態(tài):"+originator.toString());
        //創(chuàng)建一個備忘錄
        Caretaker caretaker=new Caretaker();
        //創(chuàng)建兩個備份
        caretaker.setMementoHashMap("001",originator.createMemento());
        caretaker.setMementoHashMap("002",originator.createMemento());
       
        originator.setState1("軟件");
        originator.setState3("架構(gòu)");
        originator.setState2("優(yōu)秀");
        System.out.println("后狀態(tài):"+originator.toString());
        //恢復(fù)一份備份
        originator.restoreMemento(caretaker.getMemento("001"));

        System.out.println("原狀態(tài):"+originator.toString());
    }
}

結(jié)果就不上了。

3.結(jié)論

備忘錄模式盅蝗,嚴(yán)格來講是一個比較簡單的模式鳖链,可以把它理解成一個臨時表的作用,當(dāng)臨時表使风科,只是它的變化形式比較多而已撒轮,但我認(rèn)為記住以上的這三種情況就足夠用了。

注:本方代碼段比較多贼穆,莫怪题山。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市故痊,隨后出現(xiàn)的幾起案子顶瞳,更是在濱河造成了極大的恐慌,老刑警劉巖愕秫,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慨菱,死亡現(xiàn)場離奇詭異,居然都是意外死亡戴甩,警方通過查閱死者的電腦和手機(jī)符喝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甜孤,“玉大人协饲,你說我怎么就攤上這事〗纱ǎ” “怎么了茉稠?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長把夸。 經(jīng)常有香客問我而线,道長,這世上最難降的妖魔是什么恋日? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任膀篮,我火速辦了婚禮,結(jié)果婚禮上岂膳,老公的妹妹穿的比我還像新娘各拷。我一直安慰自己,他們只是感情好闷营,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般傻盟。 火紅的嫁衣襯著肌膚如雪速蕊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天娘赴,我揣著相機(jī)與錄音规哲,去河邊找鬼。 笑死诽表,一個胖子當(dāng)著我的面吹牛唉锌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竿奏,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袄简,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泛啸?” 一聲冷哼從身側(cè)響起绿语,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎候址,沒想到半個月后吕粹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡岗仑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年匹耕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荠雕。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡稳其,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舞虱,到底是詐尸還是另有隱情欢际,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布矾兜,位于F島的核電站损趋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏椅寺。R本人自食惡果不足惜浑槽,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望返帕。 院中可真熱鬧桐玻,春花似錦、人聲如沸荆萤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至偏竟,卻和暖如春煮落,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踊谋。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工蝉仇, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人殖蚕。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓轿衔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親睦疫。 傳聞我的和親對象是個殘疾皇子害驹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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