Memento 模式


您的使用者可以對應用程式屬性進行設定,假設應用程式相關屬性是放在Application物件中,您想要設計一個屬性備份還原功能,如果您直接在Application物件中建立備份復原機制,這會使得物件本身的職責加重,與其在物件內建立備份復原機制,不如將備份復原機制從物件中脫離出來,如此一來還有可能在別的地方重用備份復原機制。

您可使用一個Backup物件來封裝想要備份的資訊,Application會負責建立Backup物件,建立的Backup物件則交由一個Recovery物件來管理,一個可能的設計方式如下所示:
import java.util.*;

class Backup {
final String state;
final Date date;
Backup(String state) {
this.state = state;
this.date = new Date();
}
}

class Application {
private String state = "default setting";

Backup backup() {
return new Backup(state);
}
void recover(Backup backup) {
this.state = backup.state;
}

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

class Recovery {
private List<Backup> backups = new ArrayList<Backup>();
void add(Backup backup) {
backups.add(backup);
}
Backup retrieve(Date date) {
for(Backup backup : backups) {
if(backup.date.equals(date)) {
backups.remove(backup);
return backup;
}
}
return null;
}
}

在改變Application狀態設定之前,您可以使用backup()方法對狀態進行備份,Recovery則管理儲存Backup物件,之後若想要還原Application狀態,則可以從Recovery取得備份資訊進行還原。例如:
public class Main {
public static void main(String[] args) {
Application application = new Application();
Recovery recovery = new Recovery();

System.out.println(application.getState());

Backup backup = application.backup(); // 建立備忘
recovery.add(backup); // 加入備忘錄

application.setState("customer setting");
System.out.println(application.getState());

Date date = backup.date; // 假設 date 是使用者自行設定所要取得的還原時間!
application.recover(recovery.retrieve(date)); // 取得備忘來還原
System.out.println(application.getState());
}
}

這是Memento模式的一個例子。Memento模式在Originator中保留一個Memento成員,這個Memento可以包括Originator的成員資訊,在外部的話, Memento可以由一個Caretaker維護,每對Originator作一個動作,Caretaker就保留Originator動作前的成員狀 態,如果以後要復原物件狀態,只要從Caretaker取回Memento物件,對Originator進行狀態復原。

Memento模式的 UML 類別結構圖如下所示:


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

範例中Application即Originator的角色,Backup即Memento的角色,Recovery即Caretaker的角色。

如果使用Python來示範方才的範例:
import time
class Backup:
def __init__(self, state):
self.state = state
self.date = time.ctime()

class Application:
def __init__(self):
self.state = "default setting"

def backup(self):
return Backup(self.state)

def recover(self, backup):
self.state = backup.state

class Recovery:
def __init__(self):
self.backups = []

def add(self, backup):
self.backups.append(backup)

def retrieve(self, date):
for backup in self.backups:
if backup.date == date:
self.backups.remove(backup)
return backup
return None

application = Application()
recovery = Recovery()

print(application.state)

backup = application.backup() # 建立備忘
recovery.add(backup) # 加入備忘錄

application.state = "customer setting"
print(application.state)

date = backup.date; # 假設 date 是使用者自行設定所要取得的還原時間!
application.recover(recovery.retrieve(date)) # 取得備忘來還原
print(application.state)