Dashboard > Design Patterns > GoF - Design Patterns > Memento
Design Patterns Log In   View a printable version of the current page.
Memento
Added by minqing, last edited by koji lin on Sep 26, 2004  (view change)
Labels: 
(None)

資料來源: Java 技術論壇
作者: 良葛格(caterpillar)

物件導向程式強調封裝性,在可以不曝露內部狀態的情況下,就儘可能不曝露內部狀態,以提高程式的安全性,然
而有時候,我們想要保留物件的內部狀態,但即然物件已經封裝了內部資訊,我又要如何儲存其內部狀態,又不破壞其封裝性呢?這似乎是兩相矛盾的事。

有些物件可能在建立時,依您給它的建構參數來設定初始狀態,除此之外,您並沒有任何的方式來設定其內部成員,例如setName()之類的方法,然而隨著程式的運行,該物件可能會自行改變內部狀態,或是由於其它的操作或導致狀態改變,您希望您的程式中對該物件有復原機制,能夠隨時儲存某個時間點的狀態,並在適當的時候可以取出還原,這個時候您可以使用備忘錄(Memento)模式來達成這個功能。

備忘錄模式由原發者(Originator)在物件內創造一個備忘錄(Memento)物件,由於是在物件內創造的,我們可以將原發者的內部物件記錄在備忘錄物件中,如果以後要復原物件狀態,只要取回備忘錄物件,就可以進行復原,使用者不會直接接觸到物件的內部狀態,封裝性也就可以保留。

備忘錄模式的UML類別結構圖如下所示:

圖中的Caretaker是用來保留原發者所創造的備忘錄物件,以供日後復原時取回,state表示一個內部狀態,內部狀態多時,也可以將之組織為一個類別。

下面提供一個簡單的實作,看看如何實現備忘錄模式:

public class Originator {
    private String _name;
    private String _phone;
    
    public Originator(String name, String phone) {
        _name = name;
        _phone = phone;
    }

    // Some operations make state changed
    public void someOperation() {
        _name = "noboby";
        _phone = "911-911";
    }
    
    // recover object's state
    public void setMemento(Memento m) {
        _name = m.getName();
        _phone = m.getPhone();
    }

    public Memento createMemento() {
        return new Memento(_name, _phone);
    }
     
    public void showInfo() {
        System.out.println("Name: " + _name +
                         "\nPhone: " + _phone + "\n");
    }
}
public class Memento {
    private String _name;
    private String _phone;

    public Memento(String name, String phone) {
        _name = name;
        _phone = phone;
    }
    
    public String getName() {
        return _name;
    }

    public String getPhone() {
        return _phone;
    }

    public void setName(String name) {
        _name = name;
    }
 
    public void setPhone(String phone) {
        _phone = phone;
    }
}
public class Caretaker {
    private Memento _memento;

    public void setMemento(Memento memento) {
        _memento = memento;
    }

    public Memento getMemento() {
        return _memento;
    }
}
public class Main {
    public static void main(String[] args) {
        Originator originator = new Originator("Justin", "888-8888");
        Caretaker caretaker = new Caretaker();
    
    // save object's memento
        caretaker.setMemento(originator.createMemento());
  
    originator.showInfo();
        // some operations make the object's state changed
        originator.someOperation();
        originator.showInfo();
  
    // use memento to recover object's state
        originator.setMemento(caretaker.getMemento());
        originator.showInfo();
    }
}


執行的結果如下:

Name: Justin
Phone: 888-8888

Name: noboby
Phone: 911-911

Name: Justin
Phone: 888-8888


我們可以使用命令(Command)模式來實作Redo/Undo的功能,然而有一個前題是您必須儲存被操作物件的狀態,這時候我們就可以使用備忘錄模式,將操作前的物件狀態記錄下來,並記錄所使用的命令,當要實現Undo時,只要取回備忘錄物件以復原物件狀態即可,至於Redo,則是取回命令物件並操作,不過由於額外使用一些空間來記錄這些物件,所以會消耗大量的記憶體是其缺點。

Site powered by a free Open Source Project / Non-profit License (more) of Confluence - the Enterprise wiki.
Learn more or evaluate Confluence for your organisation.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.1.5a Build:#411 Mar 16, 2006) - Bug/feature request - Contact Administrators