備忘錄模式(Cemento Pattern)

一、定義
在不破壞封裝性的前提下, 捕獲一個對象的內部狀態, 並在該對象之外保存這個狀態。 這樣以後就可將該對象恢復到原先保存的狀態。
二、示例
相信我們在工作中都用過word編輯器吧,word中有一項功能,當我們的編輯出錯時,Ctrl+Z可以使我們回退到以前的文本,這樣我們就可以在以前文本的基礎上繼續編輯,而不需要從頭開始編輯。現在我們應用備忘錄模式實現一下這個功能。
類圖如下:
這裏寫圖片描述

首先我們要創建一個文檔編輯器,負責編輯文本,備份文本,及恢復備份的文本

package memento;
/**
 * 文檔編輯器
 */
public class Word {

    private String text;//文本

    //獲取文本內容
    public String getText() {
        return text;
    }

    //編輯文本內容
    public void setText(String text) {
        this.text = text;
    }

    //創建一個備忘錄,並且保存文本到備忘錄中
    public Memento createMemento(String text){
        return new Memento(text);
    }

    //從備忘錄中恢復文本
    public void restoreText(Memento memento){
        this.setText(memento.getText());
    }

}

其次我們要創建一個備忘錄類,保存我們備份的文本

package memento;
/**
 * 備忘錄類
 */
public class Memento {

    private String text;//備份的文本

    //構造方法,傳入要備份的文本
    public Memento(String text) {
        super();
        this.text = text;
    }

    //獲取備份文本
    public String getText() {
        return text;
    }

    //備份文本到備忘錄中
    public void setText(String text) {
        this.text = text;
    }

}

我們還要創建一個備忘錄管理類,來管理我們的備忘錄

package memento;

import java.util.HashMap;

/**
 * 備忘錄管理類
 */
public class MementoManager {

    private HashMap<String,Memento> mementos=new HashMap<String,Memento>();//保存備忘錄,根據時間來區分備忘錄

    //添加備忘錄
    public void addMemento(String time,Memento memento){
        if(this.mementos==null){
            this.mementos=new HashMap<String,Memento>();
        }
        this.mementos.put(time,memento);
    }

    //根據時間獲取備忘錄
    public Memento getMemento(String time){
        return this.mementos.get(time);
    }
}

測試類

package memento;
/**
 * 場景類
 */
public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Word word=new Word();//新建一個文檔
        MementoManager manager=new MementoManager();//備份管理器
        word.setText("第一行文本");//寫入第一行文本
        System.out.println("寫入文本,當前文本:\n"+word.getText());
        manager.addMemento("15:38",word.createMemento(word.getText()));//創建一個備份並添加到管理器中
        System.out.println("======備份======");
        word.setText("第一行文本\n第二行文本");//寫入第二行文本
        System.out.println("寫入文本,當前文本:\n"+word.getText());
        word.restoreText(manager.getMemento("15:38"));
        System.out.println("恢復,當前文本:\n"+word.getText());
    }

}

運行結果:

這裏寫圖片描述

三、角色

通俗地說, 備忘錄模式就是一個對象的備份模式, 提供了一種程序數據的備份方法。一般來說,有三種角色:

  1. Originator 發起人角色
    記錄當前時刻的內部狀態, 負責定義哪些屬於備份範圍的狀態, 負責創建和恢復備忘錄數據。例如上例中的:Word.class。
  2. Cementor 備忘錄角色
    負責存儲Originator發起人對象的內部狀態, 在需要的時候提供發起人需要的內部狀態。例如上例中的:Cementor.class。
  3. CementorManager 備忘錄管理員角色
    對備忘錄進行管理、 保存和提供備忘錄。例如上例中的:CementorManager.class。

四、使用場景

  • 需要保存和恢復數據的相關狀態場景。
  • 提供一個可回滾( rollback) 的操作; 比如Word中的CTRL+Z組合鍵, IE瀏覽器中的後退按鈕, 文件管理器上的backspace鍵等。
  • 需要監控的副本場景中。 例如要監控一個對象的屬性, 但是監控又不應該作爲系統的主業務來調用, 它只是邊緣應用, 即使出現監控不準、 錯誤報警也影響不大, 因此一般的做法是備份一個主線程中的對象, 然後由分析程序來分析。

五、注意事項

  1. 備忘錄的生命期
    備忘錄創建出來就要在“最近”的代碼中使用, 要主動管理它的生命週期, 建立就要使用, 不使用就要立刻刪除其引用, 等待垃圾回收器對它的回收處理。
  2. 備忘錄的性能
    不要在頻繁建立備份的場景中使用備忘錄模式( 比如一個for循環中) , 原因有二: 一是控制不了備忘錄建立的對象數量; 二是大對象的建立是要消耗資源的, 系統的性能需要考慮。

六、clone方式的備忘錄

原型模式的時候,我們可以通過複製的方法產生一個對象的內部狀態,這個一個很好的方法,我們只需要發起者角色實現Cloneable接口:

package memento;
/**
 * 實現Cloneable接口,既是發起者角色,也是備忘錄角色
 */
public class CloneableWord implements Cloneable{

    private String text;//文本

    private CloneableWord backup;//備份對象

    //獲取文本內容
    public String getText() {
        return text;
    }

    //設置文本內容
    public void setText(String text) {
        this.text = text;
    }

    //創建備份
    public void createMemento(){
        this.backup=(CloneableWord) this.clone();//通過clone()方法複製對象的狀態給備份對象
    }

    //從備份中恢復文本
    public void restoreText(){
        this.setText(this.backup.getText());//從備份對象恢復對象狀態
    }

    @Override
    protected Object clone() {
        try{
            return (CloneableWord)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return null;
    }
}

增加一個備份對象,通過clone()方法完整複製一個對象的狀態到備份對象,需要的時候再通過備份對象還原。這樣,CloneableWord既是發起人角色,也是備忘錄角色。

package memento;
/**
 * 場景類
 */
public class Client {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        CloneableWord word=new CloneableWord();//新建一個文檔
        word.setText("第一行文本");//寫入第一行文本
        System.out.println("寫入文本,當前文本:\n"+word.getText());
        word.createMemento();//備份
        System.out.println("======備份======");
        word.setText("第一行文本\n第二行文本");//寫入第二行文本
        System.out.println("寫入文本,當前文本:\n"+word.getText());
        word.restoreText();//恢復文本
        System.out.println("恢復,當前文本:\n"+word.getText());
    }

}

運行結果:
這裏寫圖片描述

與前面實現方式完全一致。

項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章