備忘錄模式

備忘錄模式(),是一种软件设计模式,它在不違反封裝的前提下,捕獲並外部化一個物件的內部狀態,使得以後可以將物件回復到其先前的状态(撤消變更),使用此模式的例子還有版本控制,以及自定义序列化。

概述
備忘錄設計模式是著名的《設計模式:可復用物件導向軟體的基礎》所收錄的二十三個模式之一。此書描述了如何解決常見的設計問题,以設計出彈性且異重用的物件導向软件(亦即更容易實現、更改、测试、维护和重用的物件)。備忘錄模式是由Noah Thompson、David Espiritu和Drew Clinkenbeard博士为惠普早期产品创建的。

備忘錄模式主要由三個角色來實現:發起者、看守員和備忘錄。發起者是具有內部狀態的角色。看守員可以對發起者做一些操作,尤其是希望能够撤消變更的操作。看守員首先向發起者索要一件備忘錄。然后它执行此操作(或操作序列)。若要回滚到操作之前的状态,它可以将備忘錄返回给发起者。備忘錄本身是一个不透明的物件(看守員不能或不應更改此物件)。使用此模式時,應注意發起者是否可能更改其他物件或資源——備忘錄模式是對單一物件進行操作。

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

備忘錄設計模式提供的解决方案:让一个物件(發起者)本身负责,将其内部状态保存到(備忘錄)物件,並且从(備忘錄)物件回復到以前的狀態,只有產生此備忘錄的發起者可以存取它;客户端(看守員)可以向發起者請求備忘錄(以保存發起者的内部状态)并将備忘錄传递回發起者(以恢复到先前的状态)。这使得外部能够在不破壞其封装的情况下,保存和回復發起者的内部狀態。

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

结构
UML 類別圖和序列圖
在上面的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程序说明了備忘錄模式的“撤消變更”用法。
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 savedStates = new ArrayList();

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));
}
}
输出是:
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#
備忘錄模式允许人們在不違反封装的情况下捕获物件的内部状态,以便以后可以根據需求來撤消變更/回復變更。從这里可以看出,備忘錄物件实际上用于恢复物件中所做的更改。

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();

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例子:

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类的特定属性的历史数据,用作纪念品的对象在例子中只拥有充当唯一标识符的一项简单功能。下面是其执行:

>> 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
// 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

参见
*中介者模式

参考

评论 (0)

  • 还没有评论,来抢沙发吧。