天天看點

認真學習設計模式之備忘錄模式(Memento Pattern)

【1】概述

備忘錄模式(Memento Pattern)在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态。這樣以後就可将該對象恢複到原先儲存的狀态。

可以這裡了解備忘錄模式:現實生活中的備忘錄是用來記錄某些要去做的事情,或者是記錄已經達成的共同意見的事情,以防忘記了。而在軟體層面,備忘錄模式有着相同的含義,備忘錄對象主要用來記錄一個對象的某種狀态,或者某些資料,當要做回退時,可以從備忘錄對象裡擷取原來的資料進行恢複操作

備忘錄模式屬于行為型模式。

UML原理圖

認真學習設計模式之備忘錄模式(Memento Pattern)

對原理類圖的說明-即(備忘錄模式的角色及職責)

  • originator : 對象(需要儲存狀态的對象)
  • Memento : 備忘錄對象,負責儲存好記錄,即Originator 内部狀态
  • Caretaker: 守護者對象,負責儲存多個備忘錄對象, 使用集合管理,提高效率

說明:如果希望儲存多個originator 對象的不同時間的狀态,也可以,隻需要要HashMap<String, 集合>

【2】案例分析

遊戲角色狀态恢複問題

遊戲角色有攻擊力和防禦力,在大戰Boss 前儲存自身的狀态(攻擊力和防禦力),當大戰Boss 後攻擊力和防禦力下降,從備忘錄對象恢複到大戰前的狀态。

① 傳統方式

傳統方式可能如下解決:

認真學習設計模式之備忘錄模式(Memento Pattern)

傳統的方式的問題分析

  • 一個對象,就對應一個儲存對象狀态的對象, 這樣當我們遊戲的對象很多時,不利于管理,開銷也很大.
  • 傳統的方式是簡單地做備份,new 出另外一個對象出來,再把需要備份的資料放到這個新對象,但這就暴露了對象内部的細節

我們的解決方案: => 備忘錄模式

② 備忘錄模式

① 定義守護者對象Caretaker

public class Caretaker {
  
  //在List 集合中會有很多的備忘錄對象
  private List<Memento> mementoList = new ArrayList<Memento>();
  
  public void add(Memento memento) {
    mementoList.add(memento);
  }
  
  //擷取到第index個Originator 的 備忘錄對象(即儲存狀态)
  public Memento get(int index) {
    return mementoList.get(index);
  }
}      

② 定義備忘錄對象與原始對象

public class Memento {
  private String state;

  //構造器
  public Memento(String state) {
    super();
    this.state = state;
  }

  public String getState() {
    return state;
  }
}

public class Originator {

  private String state;//狀态資訊

  public String getState() {
    return state;
  }

  public void setState(String state) {
    this.state = state;
  }
  
  //編寫一個方法,可以儲存一個狀态對象 Memento
  //是以編寫一個方法,傳回 Memento
  public Memento saveStateMemento() {
    return new Memento(state);
  }
  
  //通過備忘錄對象,恢複狀态
  public void getStateFromMemento(Memento memento) {
    state = memento.getState();
  }
}      

③ 用戶端測試

public class Client {

  public static void main(String[] args) {

    Originator originator = new Originator();
    Caretaker caretaker = new Caretaker();
    
    originator.setState(" 狀态#1 攻擊力 100 ");
    
    //儲存了目前的狀态
    caretaker.add(originator.saveStateMemento());
    
    originator.setState(" 狀态#2 攻擊力 80 ");
    
    caretaker.add(originator.saveStateMemento());
    
    originator.setState(" 狀态#3 攻擊力 50 ");
    caretaker.add(originator.saveStateMemento());
    
    System.out.println("目前的狀态是 =" + originator.getState());
    
    //希望得到狀态 1, 将 originator 恢複到狀态1
    
    originator.getStateFromMemento(caretaker.get(0));
    System.out.println("恢複到狀态1 , 目前的狀态是");
    System.out.println("目前的狀态是 =" + originator.getState());
  }
}      

列印結果:

目前的狀态是 = 狀态#3 攻擊力 50 
恢複到狀态1 , 目前的狀态是
目前的狀态是 = 狀态#1 攻擊力 100      

③ 備忘錄模式的注意事項和細節

  • 給使用者提供了一種可以恢複狀态的機制,可以使使用者能夠比較友善地回到某個曆史的狀态
  • 實作了資訊的封裝,使得使用者不需要關心狀态的儲存細節
  • 如果類的成員變量過多,勢必會占用比較大的資源,而且每一次儲存都會消耗一定的記憶體, 這個需要注意
  • 适用的應用場景:1、後悔藥。2、打遊戲時的存檔。3、Windows 裡的ctri + z。4、IE 中的後退。4、資料庫的事務管理