Memento 是一種行為設計模式,可讓您保存和恢復對象的先前狀態(tài),而無需透露其實現(xiàn)的細節(jié)。
假設您正在創(chuàng)建一個文本編輯器應用程序。除了編寫文本之外,文本編輯器的基本要求之一是能夠撤消所做的更改。
因此,您決定使用直接實現(xiàn),在該實現(xiàn)中,在執(zhí)行任何操作之前,應用程序會記錄所有對象的狀態(tài)并將其存儲在某處。因此,當用戶決定恢復操作時,應用程序會從歷史記錄中檢索最新的快照。
對象中所有字段的值都需要復制到存儲中。但是,這只有在對象放寬對其內(nèi)容的訪問限制時才有可能,而現(xiàn)實生活中并非如此。
Memento 設計模式
損壞的封裝是我們剛剛遇到的問題的原因。對象有時會嘗試做比他們應該做的更多的事情。為了收集執(zhí)行特定操作所需的數(shù)據(jù),它們會侵入其他對象的私有空間,而不是讓這些對象執(zhí)行操作。
Memento 模式將創(chuàng)建狀態(tài)快照的責任委托給狀態(tài)的實際所有者,即創(chuàng)建者對象。因此,其他對象不應該嘗試復制編輯器的狀態(tài)。
該模式建議將對象狀態(tài)的副本存儲在特殊的紀念品中。紀念品的內(nèi)容只能由產(chǎn)生它的對象訪問。 Mementos 必須通過有限的接口與其他對象通信,該接口允許獲取快照的元數(shù)據(jù),但不能獲取原始對象的狀態(tài)。
在這種限制性政策下,紀念品可以存儲在其他對象中,通常稱為看守者??词卣邔o念品的訪問權限有限,因此它無法更改其狀態(tài)。同樣,發(fā)起者可以訪問備忘錄中的所有字段,允許它隨意恢復其先前的狀態(tài)。
在實現(xiàn)撤銷時,命令和備忘錄設計模式可以一起使用。命令負責對目標對象執(zhí)行各種操作,而備忘錄在命令執(zhí)行之前記錄該對象的狀態(tài)。
UML 類圖
實施步驟
源代碼實現(xiàn)
需要時,Editor (Originator) 類可以創(chuàng)建其自身狀態(tài)的快照以及從快照中恢復其狀態(tài)。
Memento 是一個值對象,充當發(fā)起者當前狀態(tài)的快照。使備忘錄不可變并且只通過構造函數(shù)傳遞一次數(shù)據(jù)是很常見的。
package com.learncsdesign;public class Editor {private String text;private int cursorX;private int cursorY;private int selectionWidth;public void setText(String text) {this.text = text;}public String getText() {return text;}public void setCursor(int cursorX, int cursorY) {this.cursorX = cursorX;this.cursorY = cursorY;}public void setSelectionWidth(int selectionWidth) {this.selectionWidth = selectionWidth;}public Snapshot save() {return new Snapshot(text, cursorX, cursorY, selectionWidth);}public void restore(Snapshot snapshot) {setText(snapshot.getText());setCursor(snapshot.getCursorX(), snapshot.getCursorY());setSelectionWidth(snapshot.getSelectionWidth());}class Snapshot {private final String text;private final int cursorX;private final int cursorY;private final int selectionWidth;private Snapshot(String text, int cursorX, int cursorY, int selectionWidth) {this.text = text;this.cursorX = cursorX;this.cursorY = cursorY;this.selectionWidth = selectionWidth;}public String getText() {return text;}public int getCursorX() {return cursorX;}public int getCursorY() {return cursorY;}public int getSelectionWidth() {return selectionWidth;}}}
除了知道何時以及為什么要捕獲發(fā)起者的狀態(tài)之外,看守者還知道何時恢復它。 看守者可以通過存儲紀念品來跟蹤發(fā)起者的歷史。 當發(fā)起者必須及時返回時,看守者從堆棧中檢索最頂部的 memento 并將其傳遞給發(fā)起者的恢復方法。
package com.learncsdesign;import java.util.Stack;import com.learncsdesign.Editor.Snapshot;public class CareTaker {private static Stack snapshots = new Stack();private static void doBackup(Editor editor) {snapshots.push(editor.save());}private static void undo(Editor editor) {if (!snapshots.isEmpty()) {editor.restore(snapshots.pop());}}public static void main(String[] args) {Editor editor = new Editor();editor.setText(“Hello World!”);System.out.println(“Editor text: ” + editor.getText());doBackup(editor);editor.setText(“Hello Medium!”);System.out.println(“Editor modified text: ” + editor.getText());undo(editor);System.out.println(“Editor Restored text: ” + editor.getText());}}
memento 類嵌套在這個實現(xiàn)中的 originator 類中。發(fā)起者可以訪問備忘錄的字段和方法,即使它們已被聲明為私有。盡管如此,看守者對紀念品的字段和方法的訪問非常有限,這允許它在不改變其狀態(tài)的情況下將紀念品存儲在堆棧上。
何時應用 Memento 設計模式
- 當您需要創(chuàng)建對象狀態(tài)的快照以便能夠將其恢復到以前的狀態(tài)時,請使用 Memento 模式。
- 每當直接訪問對象的字段違反其封裝時,請使用此模式。 Memento 使對象本身負責拍攝其狀態(tài)的快照??煺詹荒鼙蝗魏纹渌麑ο笞x取,因此原始對象的狀態(tài)數(shù)據(jù)是安全可靠的。
Memento 設計模式的優(yōu)點
- 可以在不違反其封裝的情況下創(chuàng)建對象狀態(tài)的快照。
- 讓看守者維護發(fā)起者狀態(tài)的歷史,以便簡化發(fā)起者的代碼。
如果你喜歡這篇文章,你知道該怎么做
關注七爪網(wǎng),獲取更多APP/小程序/網(wǎng)站源碼資源!