Memento 是一種行為設(shè)計(jì)模式,可讓您保存和恢復(fù)對(duì)象的先前狀態(tài),而無(wú)需透露其實(shí)現(xiàn)的細(xì)節(jié)。
假設(shè)您正在創(chuàng)建一個(gè)文本編輯器應(yīng)用程序。除了編寫(xiě)文本之外,文本編輯器的基本要求之一是能夠撤消所做的更改。
因此,您決定使用直接實(shí)現(xiàn),在該實(shí)現(xiàn)中,在執(zhí)行任何操作之前,應(yīng)用程序會(huì)記錄所有對(duì)象的狀態(tài)并將其存儲(chǔ)在某處。因此,當(dāng)用戶(hù)決定恢復(fù)操作時(shí),應(yīng)用程序會(huì)從歷史記錄中檢索最新的快照。
對(duì)象中所有字段的值都需要復(fù)制到存儲(chǔ)中。但是,這只有在對(duì)象放寬對(duì)其內(nèi)容的訪(fǎng)問(wèn)限制時(shí)才有可能,而現(xiàn)實(shí)生活中并非如此。
Memento 設(shè)計(jì)模式
損壞的封裝是我們剛剛遇到的問(wèn)題的原因。對(duì)象有時(shí)會(huì)嘗試做比他們應(yīng)該做的更多的事情。為了收集執(zhí)行特定操作所需的數(shù)據(jù),它們會(huì)侵入其他對(duì)象的私有空間,而不是讓這些對(duì)象執(zhí)行操作。
Memento 模式將創(chuàng)建狀態(tài)快照的責(zé)任委托給狀態(tài)的實(shí)際所有者,即創(chuàng)建者對(duì)象。因此,其他對(duì)象不應(yīng)該嘗試復(fù)制編輯器的狀態(tài)。
該模式建議將對(duì)象狀態(tài)的副本存儲(chǔ)在特殊的紀(jì)念品中。紀(jì)念品的內(nèi)容只能由產(chǎn)生它的對(duì)象訪(fǎng)問(wèn)。 Mementos 必須通過(guò)有限的接口與其他對(duì)象通信,該接口允許獲取快照的元數(shù)據(jù),但不能獲取原始對(duì)象的狀態(tài)。
在這種限制性政策下,紀(jì)念品可以存儲(chǔ)在其他對(duì)象中,通常稱(chēng)為看守者。看守者對(duì)紀(jì)念品的訪(fǎng)問(wèn)權(quán)限有限,因此它無(wú)法更改其狀態(tài)。同樣,發(fā)起者可以訪(fǎng)問(wèn)備忘錄中的所有字段,允許它隨意恢復(fù)其先前的狀態(tài)。
在實(shí)現(xiàn)撤銷(xiāo)時(shí),命令和備忘錄設(shè)計(jì)模式可以一起使用。命令負(fù)責(zé)對(duì)目標(biāo)對(duì)象執(zhí)行各種操作,而備忘錄在命令執(zhí)行之前記錄該對(duì)象的狀態(tài)。
UML 類(lèi)圖
實(shí)施步驟
源代碼實(shí)現(xiàn)
需要時(shí),Editor (Originator) 類(lèi)可以創(chuàng)建其自身狀態(tài)的快照以及從快照中恢復(fù)其狀態(tài)。
Memento 是一個(gè)值對(duì)象,充當(dāng)發(fā)起者當(dāng)前狀態(tài)的快照。使備忘錄不可變并且只通過(guò)構(gòu)造函數(shù)傳遞一次數(shù)據(jù)是很常見(jiàn)的。
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;}}}
除了知道何時(shí)以及為什么要捕獲發(fā)起者的狀態(tài)之外,看守者還知道何時(shí)恢復(fù)它。 看守者可以通過(guò)存儲(chǔ)紀(jì)念品來(lái)跟蹤發(fā)起者的歷史。 當(dāng)發(fā)起者必須及時(shí)返回時(shí),看守者從堆棧中檢索最頂部的 memento 并將其傳遞給發(fā)起者的恢復(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 類(lèi)嵌套在這個(gè)實(shí)現(xiàn)中的 originator 類(lèi)中。發(fā)起者可以訪(fǎng)問(wèn)備忘錄的字段和方法,即使它們已被聲明為私有。盡管如此,看守者對(duì)紀(jì)念品的字段和方法的訪(fǎng)問(wèn)非常有限,這允許它在不改變其狀態(tài)的情況下將紀(jì)念品存儲(chǔ)在堆棧上。
何時(shí)應(yīng)用 Memento 設(shè)計(jì)模式
- 當(dāng)您需要?jiǎng)?chuàng)建對(duì)象狀態(tài)的快照以便能夠?qū)⑵浠謴?fù)到以前的狀態(tài)時(shí),請(qǐng)使用 Memento 模式。
- 每當(dāng)直接訪(fǎng)問(wèn)對(duì)象的字段違反其封裝時(shí),請(qǐng)使用此模式。 Memento 使對(duì)象本身負(fù)責(zé)拍攝其狀態(tài)的快照??煺詹荒鼙蝗魏纹渌麑?duì)象讀取,因此原始對(duì)象的狀態(tài)數(shù)據(jù)是安全可靠的。
Memento 設(shè)計(jì)模式的優(yōu)點(diǎn)
- 可以在不違反其封裝的情況下創(chuàng)建對(duì)象狀態(tài)的快照。
- 讓看守者維護(hù)發(fā)起者狀態(tài)的歷史,以便簡(jiǎn)化發(fā)起者的代碼。
如果你喜歡這篇文章,你知道該怎么做
關(guān)注七爪網(wǎng),獲取更多APP/小程序/網(wǎng)站源碼資源!