簡介 備忘錄模式(Memento Pattern)是一種結構型設計模式。這種模式就是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並放在外部存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。 備忘錄模式的角色有三個:備忘 ...
簡介
備忘錄模式(Memento Pattern)是一種結構型設計模式。這種模式就是在不破壞封裝的條件下,將一個對象的狀態捕捉(Capture)住,並放在外部存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。備忘錄模式常常與命令模式和迭代子模式一同使用。
備忘錄模式的角色有三個:備忘錄(Memento)角色、發起人(Originator)角色、負責人(Caretaker)角色
備忘錄模式是由發起人(Originator)對象負責生成狀態快照,其他對象不可修改狀態。再將對象狀態的副本存儲在一個名為備忘錄(Memento)的特殊對象中。除了創建備忘錄的對象外,任何對象都不能訪問備忘錄的內容。其他對象必須使用指定介面與備忘錄進行交互,它們可以獲取快照的元數據(創建時間和操作名稱等),但不能獲取快照中原始對象的狀態。
這種限制策略允許你將備忘錄保存在通常被稱為負責人(Caretakers)的對象歷史中。由於負責人僅通過受限介面與備忘錄互動,故其無法修改存儲在備忘錄內部的狀態。同時,發起人擁有對備忘錄所有成員的訪問許可權,從而能隨時恢復其以前的某個狀態。
作用
- 給用戶提供了一種可以恢復狀態的機制,可以使用戶能夠比較方便地回到某個歷史的狀態。
- 實現了內部狀態的封裝,除了創建它的發起人之外,其他對象都不能夠訪問這些狀態信息,也不需要關心狀態的保存細節。
- 簡化了發起人角色,發起人不需要管理和保存其內部狀態的各個備份,所有狀態信息都保存在備忘錄中,並由負責人進行管理,符合單一職責原則。
實現步驟
- 創建備忘錄Memento,用來記錄操作狀態數據的實體類。
- 創建發起人角色Originator,狀態的製造者,也是備忘錄的生成者,負責將狀態寫入到一個新備忘錄。
- 創建負責人角色Caretaker,用來保存和讀取備忘錄的歷史記錄,所有備忘錄均可以保存在歷史中,以便恢復。
- 客戶調用方通過Originator來生成備忘錄,再通過Caretaker讀取和恢復備忘錄歷史記錄。
UML
Java代碼
具體備忘錄
// Memento.java 備忘錄(Memento)角色,負責存儲發起人傳入的狀態
public class Memento {
private String state;
public Memento(String state) {
System.out.println(this.getClass().getName() + "::Memento() [state = " + state + "]");
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
發起人
// Originator.java 發起人(Originator)負責生成狀態快照,即利用一個新備忘錄對象將自己的內部狀態存儲起來
public class Originator {
private String state;
// 每次創建一個新備忘錄來保存狀態
public Memento saveMemento() {
System.out.println(this.getClass().getName() + "::saveMemento() [state = " + state + "]");
return new Memento(state);
}
// 從備忘錄中恢復狀態
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
負責人類
// Caretaker.java 負責人(Caretaker)角色,只負責保存備忘錄記錄,不能修改備忘錄對象的內容
public class Caretaker {
// 備忘錄可以是一個記錄,也可以就是一個對象,根據業務場景設置
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento memento) {
System.out.println(this.getClass().getName() + "::add() [memento = " + memento.getClass().getName() + "]");
mementoList.add(memento);
}
public Memento get(int index) {
return mementoList.get(index);
}
public List<Memento> getMementoList() {
return this.mementoList;
}
}
測試調用
/*
* 備忘錄模式是在不暴露對象實現細節的情況下保存和恢復對象之前的狀態。
* 先聲明發起人Originator,再聲明負責人Caretaker,發起人生成備忘錄Memento
* 通過負責人則保存備忘錄歷史記錄,讀取備忘錄由負責人來完成。
*/
Originator originator = new Originator();
Caretaker careTaker = new Caretaker();
// 發起人產生一個狀態
originator.setState("state1");
// 覆蓋了狀態,那麼前面的狀態未保存
originator.setState("state2");
// 發起人生成備忘錄,一般添加時直接保存即可
Memento memento = originator.saveMemento();
// 負責人保添加備忘錄歷史記錄
careTaker.add(memento);
// 直接生成備忘錄並添加到負責人的備忘錄列表
originator.setState("state3");
careTaker.add(originator.saveMemento());
originator.setState("state4");
careTaker.add(originator.saveMemento());
System.out.println("發起人當前的狀態: " + originator.getState());
// 發起人通過負責人那裡取出狀態
originator.restoreMemento(careTaker.get(0));
System.out.println("第一個保存的狀態: " + originator.getState());
originator.restoreMemento(careTaker.get(1));
System.out.println("第二個保存的狀態: " + originator.getState());
// 遍歷全部備忘錄
for (int i = 0; i < careTaker.getMementoList().size(); i++) {
// 外部一般不直接訪問備忘錄裡面的狀態,而是逐個恢復備忘錄,再取出狀態來
originator.restoreMemento(careTaker.get(i));
System.out.println("state: " + i + ")" + originator.getState());
}
JavaScript代碼
具體備忘錄
// Memento.js 備忘錄(Memento)角色,負責存儲發起人傳入的狀態
// 備忘錄(Memento)角色,負責存儲發起人傳入的狀態
export class Memento {
constructor(state) {
console.log(this.constructor.name + '::Memento() [state = ' + state + ']')
this.state = state
}
getState() {
return this.state
}
setState(state) {
this.state = state
}
}
發起人
// Originator.js 發起人(Originator)負責生成狀態快照,即利用一個新備忘錄對象將自己的內部狀態存儲起來
import { Memento } from './Memento.js'
export class Originator {
constructor() {
this.state = undefined
}
// 每次創建一個新備忘錄來保存狀態
saveMemento() {
console.log(
this.constructor.name + '::saveMemento() [state = ' + this.state + ']'
)
return new Memento(this.state)
}
// 從備忘錄中恢復狀態
restoreMemento(memento) {
this.state = memento.getState()
}
getState() {
return this.state
}
setState(state) {
this.state = state
}
}
負責人類
// Caretaker.js 負責人(Caretaker)角色,只負責保存備忘錄記錄,不能修改備忘錄對象的內容
export class Caretaker {
constructor() {
// 備忘錄可以是一個記錄,也可以就是一個對象,根據業務場景設置
this.mementoList = []
}
add(memento) {
console.log(
this.constructor.name +
'::add() [memento = ' +
memento.constructor.name +
']'
)
this.mementoList.push(memento)
}
get(index) {
return this.mementoList[index]
}
getMementoList() {
return this.mementoList
}
}
測試調用
import { Originator } from '../src/Originator.js'
import { Caretaker } from '../src/Caretaker.js'
export function test() {
/*
* 備忘錄模式是在不暴露對象實現細節的情況下保存和恢復對象之前的狀態。
* 先聲明發起人Originator,再聲明負責人Caretaker,發起人生成備忘錄Memento
* 通過負責人則保存備忘錄歷史記錄,讀取備忘錄由負責人來完成。
*/
const originator = new Originator()
const careTaker = new Caretaker()
// 發起人產生一個狀態
originator.setState('state1')
// 覆蓋了狀態,那麼前面的狀態未保存
originator.setState('state2')
// 發起人生成備忘錄,一般添加時直接保存即可
const memento = originator.saveMemento()
// 負責人保添加備忘錄歷史記錄
careTaker.add(memento)
// 直接生成備忘錄並添加到負責人的備忘錄列表
originator.setState('state3')
careTaker.add(originator.saveMemento())
originator.setState('state4')
careTaker.add(originator.saveMemento())
console.log('發起人當前的狀態: ' + originator.getState())
// 發起人通過負責人那裡取出狀態
originator.restoreMemento(careTaker.get(0))
console.log('第一個保存的狀態: ' + originator.getState())
originator.restoreMemento(careTaker.get(1))
console.log('第二個保存的狀態: ' + originator.getState())
// 遍歷全部備忘錄
for (let i = 0; i < careTaker.getMementoList().length; i++) {
// 外部一般不直接訪問備忘錄裡面的狀態,而是逐個恢復備忘錄,再取出狀態來
originator.restoreMemento(careTaker.get(i))
console.log('state: ' + i + ')' + originator.getState())
}
}
// 執行測試
;(function () {
console.log('test start:')
test()
})()
更多語言版本
不同語言實現設計模式:https://github.com/microwind/design-pattern