<<設(shè)計(jì)模式之禪(第二版)>>——第二十四章 備忘錄模式

定義:
  • 在不破壞封裝性的前提下妓灌,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài)琅摩,并在該對(duì)象之外保存這個(gè)狀態(tài)厅各,這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)盗蟆。
備忘錄模式的通用類圖:
備忘錄模式通用類圖
/*
 * 定義備忘錄角色
 * */
public class Memento {
  // 發(fā)起人的內(nèi)部角色
  private String state = "";

  public Memento(String state) {
    // TODO Auto-generated constructor stub
    this.state = state;
  }

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

}

/*
 * 創(chuàng)建備忘錄管理者
 * */
public class Caretaker {
  private Memento memento;

  public Memento getMemento() {
    return memento;
  }

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

}
/*
 * 定義發(fā)起人
 * */
public class Originator {
  // 內(nèi)部狀態(tài)
  private String state = "";

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

  // 創(chuàng)建備忘錄
  public Memento createMemento() {
    return new Memento(this.state);
  }

  // 重新存儲(chǔ)
  public void restoreMemento(Memento memento) {
    this.setState(memento.getState());
  }
}

public class Client {
  public static void main(String[] args) {
      // 定義發(fā)起人
      Originator originator = new Originator();
      // 創(chuàng)建備忘錄管理者
      Caretaker caretaker = new Caretaker();
      // 創(chuàng)建備忘錄
      caretaker.setMemento(originator.createMemento());
      // 恢復(fù)備忘錄
      originator.restoreMemento(caretaker.getMemento());
  }
}
使用場(chǎng)景
  • 需要保存和恢復(fù)數(shù)據(jù)的相關(guān)狀態(tài)場(chǎng)景戈二。
  • 提供一個(gè)可回滾(rollback)的操作
  • 需要監(jiān)控的副本場(chǎng)景中。備份一個(gè)主線程中的對(duì)象喳资,然后由分析程序來(lái)分析
  • 數(shù)據(jù)庫(kù)連接的事務(wù)管理就是用的備忘錄模式
注意事項(xiàng):
  • 備忘錄的生命周期:創(chuàng)建出來(lái)后觉吭,要在"最近"的代碼中使用,要主動(dòng)管理它的生命周期仆邓,建立就要使用鲜滩,不是用就要立刻刪除,等待垃圾回收器對(duì)它的回收處理节值。
  • 備忘錄的性能:不要再頻繁建立備份的場(chǎng)景中使用備忘錄模式(for循環(huán))徙硅,一是控制不了備忘錄建立的對(duì)象數(shù)量,二是大對(duì)象的建立是要消耗資源的搞疗,影響性能嗓蘑。
備忘錄模式拓展:
Clone方式的備忘錄類圖拓展
public class Caretaker {
  private Originator originator;

  public Originator getOriginator() {
    return originator;
  }

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

}
public class Originator implements Cloneable {
  // 將發(fā)起者和備忘錄角色進(jìn)行合并
  private String state = "";

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

  // 創(chuàng)建備忘錄角色
  public Originator createMemento() {
    return this.clone();
  }

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

  @Override
  protected Originator clone() {
    // TODO Auto-generated method stub
    try {
        return (Originator) super.clone();
    } catch (CloneNotSupportedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
  }
}

/*
 * 去掉管理員角色,精簡(jiǎn)
 * 設(shè)計(jì)模式定義的誕生比JAVA的出世略早贴汪,沒(méi)有想到
 * java在面向?qū)ο蟮脑O(shè)計(jì)中脐往,可以實(shí)現(xiàn)把一個(gè)類封裝在另一個(gè)類中
 * */
public class OriginatorMix implements Cloneable {
  private OriginatorMix originatorMix;

  private String state = "";

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

  public void setMemento() {
    originatorMix = this.clone();
  }

  public void restoreMemento() {
    this.setState(this.originatorMix.getState());
  }

  @Override
  protected OriginatorMix clone() {
    // TODO Auto-generated method stub
    try {
        return (OriginatorMix) super.clone();
    } catch (CloneNotSupportedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return null;
  }
}

public class Cient {
  public static void main(String[] args) {
    //使用clone方式一實(shí)現(xiàn)
    Originator originator = new Originator();
    Caretaker caretaker = new Caretaker();
    
    caretaker.setOriginator(originator);//備份
    originator.restoreMemento(caretaker.getOriginator());//恢復(fù)
    //使用clone方式二實(shí)現(xiàn)
    OriginatorMix originatorMix = new OriginatorMix();
    System.out.println("init");
    originatorMix.setState("初始狀態(tài)");
    System.out.println(originatorMix.getState());
    System.out.println("memento");
    originatorMix.setMemento();
    originatorMix.setState("備份后的狀態(tài)");
    System.out.println(originatorMix.getState());
    originatorMix.restoreMemento();
    System.out.println("recover memento");
    System.out.println(originatorMix.getState());
  }
}

發(fā)起人角色融合了發(fā)起人角色和備忘錄角色,具有雙重功效扳埂。使用clone方式的備忘錄业簿,可以使用在比較簡(jiǎn)單的場(chǎng)景或者比較單一的場(chǎng)景中,盡量不要與其他的對(duì)象產(chǎn)生嚴(yán)重的耦合關(guān)系阳懂。對(duì)于深拷貝梅尤,考慮對(duì)應(yīng)的語(yǔ)句書寫。

多狀態(tài)的備忘錄模式通用類圖
/*
 * 定義備忘錄角色
 * */
public class Memento {
  // 接受HashMap作為備份狀態(tài)
  private HashMap<String, Object> hashMap;

  public HashMap<String, Object> getStateMap() {
    return hashMap;
  }

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

  // 接收一個(gè)對(duì)象岩调,建立一個(gè)備份
  public Memento(HashMap<String, Object> hashMap) {
    // TODO Auto-generated constructor stub
    this.hashMap = hashMap;
  }

}

public class Originator {
  // 內(nèi)部狀態(tài)
  private String state1 = "";
  private String state2 = "";
  private String state3 = "";

  public String getState1() {
    return state1;
  }

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

  public String getState2() {
    return state2;
  }

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

  public String getState3() {
    return state3;
  }

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

  // 創(chuàng)建一個(gè)備忘錄
  public Memento createMemo() {
    return new Memento(BeanUtils.backupProp(this));
  }

  // 恢復(fù)一個(gè)備忘錄
  public void restoreMemento(Memento _memento) {
    BeanUtils.restoreProp(this, _memento.getStateMap());
  }

  // 增加 toString
  @Override
  public String toString() {
    // TODO Auto-generated method stub
    return "state1=" + state1 + "state2=" + state2 + "state3=" + state3;
  }
}

/*
 * 創(chuàng)建備忘錄管理者
 * */
public class Caretaker {
  private Memento memento;

  public Memento getMemento() {
    return memento;
  }

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

}

public class BeanUtils {
  // 把bean的所有屬性及數(shù)值放入到Hashmap中
  public static HashMap<String, Object> backupProp(Object bean) {
    HashMap<String, Object> result = new HashMap<>();
    try {
        // 獲取Bean描述
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
        // 獲取相關(guān)的屬性描述
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            // 獲取屬性名的值
            String fieldName = propertyDescriptor.getName();
            // 讀取屬性的get方法
            Method getter = propertyDescriptor.getReadMethod();
            // 讀取屬性值
            Object fieldValue = getter.invoke(bean, new Object[] {});
            if (!fieldName.equalsIgnoreCase("class"))
                result.put(fieldName, fieldValue);
        }

    } catch (IntrospectionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return result;
  }

  // 把HashMap放到bean中
  public static void restoreProp(Object bean, HashMap<String, Object> propMap) {

    try {
        // 獲取Bean描述
        BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
        // 獲取相關(guān)的屬性描述
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String fieldName = propertyDescriptor.getName();
            // 判斷對(duì)應(yīng)的屬性是否存在
            if (propMap.containsKey(fieldName)) {
                // 寫屬性方法
                Method setter = propertyDescriptor.getWriteMethod();
                setter.invoke(bean, new Object[] { propMap.get(fieldName) });
            }

        }
    } catch (Exception e) {
        // 異常處理
    }
  }
}

public class Client {
  public static void main(String[] args) {
    // 定義出發(fā)起人
    Originator originator = new Originator();
    // 定義出備忘錄管理員
    Caretaker caretaker = new Caretaker();
    originator.setState1("1");
    originator.setState2("2");
    originator.setState3("3");
    System.out.println("初始化狀態(tài):" + originator.toString());
    // 創(chuàng)建備忘錄
    caretaker.setMemento(originator.createMemo());
    originator.setState1("6");
    originator.setState2("5");
    originator.setState3("4");
    System.out.println("更新?tīng)顟B(tài):" + originator.toString());
    // 恢復(fù)備忘錄
    originator.restoreMemento(caretaker.getMemento());
    System.out.println("恢復(fù)后的狀態(tài)為:" + originator.toString());
  }
}
  • 多備份備份
/*
 * 多備份管理員巷燥,通過(guò)hashmap作為數(shù)據(jù)存儲(chǔ)的載體
 * */
public class Caretaker {
  // 容納備忘錄的容器
  private HashMap<String, Memento> menMap = new HashMap<>();

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

  // 注意內(nèi)存溢出問(wèn)題,該備份一旦產(chǎn)生就放入到內(nèi)存中号枕,沒(méi)有銷毀的意向缰揪,
  // 所以在系統(tǒng)設(shè)計(jì)的時(shí)候,要嚴(yán)格限定備忘錄的創(chuàng)建葱淳。建議增加Map的上限
  // 否則很容易產(chǎn)生內(nèi)存溢出的情況
  public void storeMemento(String idx, Memento memento) {
    this.menMap.put(idx, memento);
  }
}

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

    caretaker.storeMemento("1", originator.createMemo());
    caretaker.storeMemento("2", originator.createMemo());

    // 恢復(fù)到指定狀態(tài)的備忘錄
    originator.restoreMemento(caretaker.getMemento("2"));
  }
}
  • 使用內(nèi)置類實(shí)現(xiàn)備忘錄
相對(duì)安全的備忘錄模式
/*
 * 全部通過(guò)接口進(jìn)行訪問(wèn)钝腺,
 * 如果你想訪問(wèn)它的屬性肯定是不行,
 * 沒(méi)有絕對(duì)的安全赞厕,
 * 通過(guò)reflect反射修改Memento的數(shù)據(jù)
 * */
public class Caretaker {
  private IMemento memento;

  public IMemento getMemento() {
    return memento;
  }

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

}

/*
 * 定義空一個(gè)備忘錄接口
 * */
public interface IMemento {

}

public class Originator {
  // 內(nèi)部狀態(tài)
  private String state = "";

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }

  // 創(chuàng)建備忘錄
  public Memento createMemento() {
    return new Memento(this.state);
  }

  // 重新存儲(chǔ)
  public void restoreMemento(IMemento _memento) {
    this.setState(((Memento) _memento).getState());
  }

  // 使用內(nèi)部類實(shí)現(xiàn)接口
  class Memento implements IMemento {
    // 發(fā)起人的內(nèi)部角色
    private String state = "";

    public Memento(String state) {
        // TODO Auto-generated constructor stub
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末艳狐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子皿桑,更是在濱河造成了極大的恐慌毫目,老刑警劉巖蔬啡,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異镀虐,居然都是意外死亡箱蟆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門粉私,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)顽腾,“玉大人,你說(shuō)我怎么就攤上這事诺核〕ぃ” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵窖杀,是天一觀的道長(zhǎng)漓摩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)入客,這世上最難降的妖魔是什么管毙? 我笑而不...
    開(kāi)封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮桌硫,結(jié)果婚禮上夭咬,老公的妹妹穿的比我還像新娘。我一直安慰自己铆隘,他們只是感情好卓舵,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著膀钠,像睡著了一般掏湾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肿嘲,一...
    開(kāi)封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天融击,我揣著相機(jī)與錄音,去河邊找鬼雳窟。 笑死尊浪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的封救。 我是一名探鬼主播拇涤,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼兴泥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起虾宇,我...
    開(kāi)封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搓彻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體旭贬,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怔接,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了稀轨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扼脐。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖奋刽,靈堂內(nèi)的尸體忽然破棺而出瓦侮,到底是詐尸還是另有隱情,我是刑警寧澤佣谐,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布肚吏,位于F島的核電站,受9級(jí)特大地震影響狭魂,放射性物質(zhì)發(fā)生泄漏罚攀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一雌澄、第九天 我趴在偏房一處隱蔽的房頂上張望斋泄。 院中可真熱鬧,春花似錦镐牺、人聲如沸炫掐。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)卒废。三九已至,卻和暖如春宙地,著一層夾襖步出監(jiān)牢的瞬間摔认,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工宅粥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留参袱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓秽梅,卻偏偏與公主長(zhǎng)得像抹蚀,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子企垦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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

  • 1 場(chǎng)景問(wèn)題# 1.1 開(kāi)發(fā)仿真系統(tǒng)## 考慮這樣一個(gè)仿真應(yīng)用环壤,功能是:模擬運(yùn)行針對(duì)某個(gè)具體問(wèn)題的多個(gè)解決方案,記...
    七寸知架構(gòu)閱讀 2,138評(píng)論 1 50
  • 定義 備忘錄模式又叫做快照模式(Snapshot Pattern)或Token模式钞诡,是對(duì)象的行為模式郑现。 備忘錄對(duì)象...
    步積閱讀 2,932評(píng)論 0 2
  • 設(shè)計(jì)模式匯總 一湃崩、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 3,922評(píng)論 1 15
  • 前世五百次回眸接箫,換來(lái)今生擦肩而過(guò) 雖然我已經(jīng)忘記了你的容顏 可是卻記得你的情深 遇到了你是我的緣攒读,我們遇到了你!是...
    思敏姐姐閱讀 1,548評(píng)論 4 8
  • 本人很汗顏辛友,是2013年年底才第一次用上了信用卡薄扁。昨天本來(lái)是還款日,但因?yàn)榭缧修D(zhuǎn)帳導(dǎo)致到帳日期延期了一天废累。這一下我...
    諸葛兔閱讀 5,524評(píng)論 0 1