簡介
備忘錄模式(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
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/550717.html
標籤:設計模式
下一篇:返回列表
