備忘錄模式

<image>

備忘錄模式（memento pattern），是一種軟體設計模式，它在不違反封裝的前提下，捕獲並外部化一個物件的內部狀態，使得以後可以將物件回復到其先前的狀態（撤消變更），使用此模式的例子還有版本控制，以及自定義序列化。

## 概述

備忘錄設計模式是著名的《設計模式：可復用物件導向軟體的基礎》所收錄的二十三個模式之一。此書描述了如何解決常見的設計問題，以設計出彈性且異重用的物件導向軟體（亦即更容易實現、更改、測試、維護和重用的物件）。備忘錄模式是由Noah Thompson、David Espiritu和Drew Clinkenbeard博士為惠普早期產品創建的。

備忘錄模式主要由三個角色來實現：發起者、看守員和備忘錄。發起者是具有內部狀態的角色。看守員可以對發起者做一些操作，尤其是希望能夠撤消變更的操作。看守員首先向發起者索要一件備忘錄。然後它執行此操作（或操作序列）。若要回滾到操作之前的狀態，它可以將備忘錄返回給發起者。備忘錄本身是一個不透明的物件（看守員不能或不應更改此物件）。使用此模式時，應注意發起者是否可能更改其他物件或資源——備忘錄模式是對單一物件進行操作。

備忘錄設計模式可以解決如下問題：物件的內部狀態須要保存在外部，以便之後可將物件回復到此狀態，又不違反物件的封裝性。問題在於，一個良好設計的物件會被封裝起來，其表示（資料結構）是隱藏在物件內部，且無法從外部存取。

備忘錄設計模式提供的解決方案：讓一個物件（發起者）本身負責，將其內部狀態保存到（備忘錄）物件，並且從（備忘錄）物件回復到以前的狀態，只有產生此備忘錄的發起者可以存取它；客戶端（看守員）可以向發起者請求備忘錄（以保存發起者的內部狀態）並將備忘錄傳遞迴發起者（以恢復到先前的狀態）。這使得外部能夠在不破壞其封裝的情況下，保存和回復發起者的內部狀態。

備忘錄模式的經典範例有偽隨機數生成器（PRNG）（每個消費者（看守員）在初始化偽隨機數生成器（發起者）時，若使用相同的種子值（備忘錄），可以產生相同的亂數序列），此外，還有有限狀態機及其狀態。

## 結構

### UML 類別圖和序列圖

<image>

在上面的UML類圖中， `Caretaker` 類別指向了 `Originator` 類別，用於保存（`createMemento()`）和恢復（`restore(memento)`）始發者的內部狀態。`Originator` 類別實現了(1) `createMemento()`，藉由創建並返回 `Memento` 物件，其記錄了發起者當前內部狀態，(2) `restore(memento)`，藉由將 `Memento` 物件傳入，以回復狀態。

UML序列圖顯示了運行時交動：(1) 保存發起者的內部狀態： `Caretaker`對象在`Originator`對象上調用`createMemento()`，創建一個`Memento`對象，保存其當前的內部狀態 ( `setState()` )，並將`Memento`返回給`Caretaker`。(2) 恢復Originator的內部狀態： `Caretaker`對`Originator`對象調用`restore(memento)`，並指定存儲應恢復狀態的`Memento`對象。`Originator`從`Memento`獲取狀態 ( `getState()` ) 以設置自己的狀態。

## 範例

### Java

以下的Java程序說明了備忘錄模式的「撤消變更」用法。

```java
import java.util.List;
import java.util.ArrayList;
class Originator {
    private String state;
    // 這個類別可以包含其它不用存在 State 中的備忘錄

    public void set(String state) {
        this.state = state;
        System.out.println("Originator: Setting state to " + state);
    }
 
    public Memento saveToMemento() {
        System.out.println("Originator: Saving to Memento.");
        return new Memento(this.state);
    }
 
    public void restoreFromMemento(Memento memento) {
        this.state = memento.getSavedState();
        System.out.println("Originator: State after restoring from Memento: " + state);
    }
 
    public static class Memento {
        private final String state;

        public Memento(String stateToSave) {
            state = stateToSave;
        }
 
        // accessible by outer class only
        private String getSavedState() {
            return state;
        }
    }
}
 
class Caretaker {
    public static void main(String[] args) {
        List<Originator.Memento> savedStates = new ArrayList<Originator.Memento>();
 
        Originator originator = new Originator();
        originator.set("State1");
        originator.set("State2");
        savedStates.add(originator.saveToMemento());
        originator.set("State3");
        // 我們可以要求多個備忘錄，並選擇要回復到那個狀態。
        savedStates.add(originator.saveToMemento());
        originator.set("State4");
 
        originator.restoreFromMemento(savedStates.get(1));   
    }
}
```

輸出是：

```output
 Originator: Setting state to State1
 Originator: Setting state to State2
 Originator: Saving to Memento.
 Originator: Setting state to State3
 Originator: Saving to Memento.
 Originator: Setting state to State4
 Originator: State after restoring from Memento: State3
```

本示例使用 String 作為狀態，它是 Java 中的不可變物件。在現實生活中，狀態幾乎總是一個可變物件，在這種情況下，必須創建狀態的副本。

必須指出的是，所展示的實現有一個缺點：它聲明了一個內部類。如果備忘錄可以適用於多個發起者，則會更好。

實現Memento 還有其他三種主要方式：

- 序列化。
- 在同一個包中聲明的類別。
- 還可以通過代理來訪問該物件，代理可以實現對物件的任何保存/恢復操作。

### C#

備忘錄模式允許人們在不違反封裝的情況下捕獲物件的內部狀態，以便以後可以根據需求來撤消變更/回復變更。從這裡可以看出，備忘錄物件實際上用於恢復物件中所做的更改。

```csharp
class Memento {
    private readonly string savedState;

    private Memento(string stateToSave) {
        savedState = stateToSave;
    }

    public class Originator {
        private string state;
        // The class could also contain additional data that is not part of the
        // state saved in the memento.

        public void Set(string state) {
            Console.WriteLine("Originator: Setting state to " + state);
            this.state = state;
        }

        public Memento SaveToMemento() {
            Console.WriteLine("Originator: Saving to Memento.");
            return new Memento(state);
        }

        public void RestoreFromMemento(Memento memento) {
            state = memento.savedState;
            Console.WriteLine("Originator: State after restoring from Memento: " + state);
        }
    }
}

class Caretaker {
    static void Main(string[] args) {
        var savedStates = new List<Memento>();

        var originator = new Memento.Originator();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);
    }
}
```

### Python

下面是Python例子：

```python
from weakref import WeakKeyDictionary, ref

class Memento():
    def __init__(self, attr):
        assert isinstance(attr, str)
        self.attr = attr
        self.backup_dict = WeakKeyDictionary()
    def __get__(self, obj, objtype=None):
        mem = lambda:None
        self.backup_dict[mem] = obj.__dict__[self.attr]
        return mem
    def __set__(self, obj, mem):
        if mem in self.backup_dict:
            obj.__dict__[self.attr] = self.backup_dict[mem]
    def __call__(self, mem):
        return self.backup_dict[mem]
        
class Originator():
    __state = None
    __memento = Memento('_Originator__state')
    __ref_memento = ref(__memento)
    def set(self, state):
        print(f"Originator: Setting state to {state}")
        self.__state = state
    def get(self):
        print(f"Originator: Current state is {self.__state}")
        return self.__state
    def save(self):
        print("Originator: Saving to Memento")
        return self.__memento
    def restore(self, mem):
        print("Originator: Restoring from Memento")
        self.__memento = mem
    def view(self, mem):
        val = self.__ref_memento()(mem)
        print(f"Originator: Saved state is {val}")

class Caretaker():
    def __init__(self, originator):
        self.mementos = []
        self.originator = originator
    def __len__(self):
        return len(self.mementos)
    def __getitem__(self, index):
        return self.mementos[index]
    def __setitem__(self, index, value):
        self.mementos[index] = value
    def __delitem__(self, index):
        del self.mementos[index]
    def add_memento(self):
        self.mementos += [self.originator.save()]

def demo():
    originator = Originator()
    caretaker = Caretaker(originator)
    originator.set("State1")
    originator.set("State2")
    caretaker.add_memento()
    originator.set("State3")
    caretaker.add_memento()
    originator.set("State4")
    originator.view(caretaker[1])
    originator.restore(caretaker[1])
    val = originator.get()
```

這裡的`Memento`類實例只能通過「紀念品」，來操作與紀念品關聯的`Originator`類的特定屬性的事件存儲歷史數據，用作紀念品的對象在例子中只擁有充當唯一標識符的一項簡單功能。下面是其執行：

```pycon
>>> demo()
Originator: Setting state to State1
Originator: Setting state to State2
Originator: Saving to Memento
Originator: Setting state to State3
Originator: Saving to Memento
Originator: Setting state to State4
Originator: Saved state is State3
Originator: Restoring from Memento
Originator: Current state is State3
```

### JavaScript

```javascript
// The Memento pattern is used to save and restore the state of an object.
// A memento is a snapshot of an object's state.
var Memento = {// Namespace: Memento
    savedState : null, // The saved state of the object.

    save : function(state) { // Save the state of an object.
        this.savedState = state;
    },

    restore : function() { // Restore the state of an object.
        return this.savedState;
    }
};

// The Originator is the object that creates the memento.
// defines a method for saving the state inside a memento.
var Originator = {// Namespace: Originator
    state : null, // The state to be stored

    // Creates a new originator with an initial state of null
    createMemento : function() { 
        return {
            state : this.state // The state is copied to the memento.
        };
    },
    setMemento : function(memento) { // Sets the state of the originator from a memento
        this.state = memento.state;
    }
};

// The Caretaker stores mementos of the objects and
// provides operations to retrieve them.
var Caretaker = {// Namespace: Caretaker
    mementos : [], // The mementos of the objects.
    addMemento : function(memento) { // Add a memento to the collection.
        this.mementos.push(memento);
    },
    getMemento : function(index) { // Get a memento from the collection.
        return this.mementos[index];
    }
};

var action_step = "Foo"; // The action to be executed/the object state to be stored.
var action_step_2 = "Bar"; // The action to be executed/the object state to be stored.

// 設置初始值
Originator.state = action_step;
Caretaker.addMemento(Originator.createMemento());// save the state to the history
console.log("Initial State: " + Originator.state); // Foo

// 改變初始值
Originator.state = action_step_2;
Caretaker.addMemento(Originator.createMemento()); // save the state to the history
console.log("State After Change: " + Originator.state); // Bar

// 回復到第一個狀態——撤消變更
Originator.setMemento(Caretaker.getMemento(0));
console.log("State After Undo: " + Originator.state); // Foo

// 回復到第二個狀態——重做變更
Originator.setMemento(Caretaker.getMemento(1));
console.log("State After Redo: " + Originator.state); // Bar
```