Memento 是一种行为设计模式,可让您保存和恢复对象的先前状态,而无需透露其实现的细节。
假设您正在创建一个文本编辑器应用程序。除了编写文本之外,文本编辑器的基本要求之一是能够撤消所做的更改。
因此,您决定使用直接实现,在该实现中,在执行任何操作之前,应用程序会记录所有对象的状态并将其存储在某处。因此,当用户决定恢复操作时,应用程序会从历史记录中检索最新的快照。
对象中所有字段的值都需要复制到存储中。但是,这只有在对象放宽对其内容的访问限制时才有可能,而现实生活中并非如此。
Memento 设计模式
损坏的封装是我们刚刚遇到的问题的原因。对象有时会尝试做比他们应该做的更多的事情。为了收集执行特定操作所需的数据,它们会侵入其他对象的私有空间,而不是让这些对象执行操作。
Memento 模式将创建状态快照的责任委托给状态的实际所有者,即创建者对象。因此,其他对象不应该尝试复制编辑器的状态。
该模式建议将对象状态的副本存储在特殊的纪念品中。纪念品的内容只能由产生它的对象访问。 Mementos 必须通过有限的接口与其他对象通信,该接口允许获取快照的元数据,但不能获取原始对象的状态。
在这种限制性政策下,纪念品可以存储在其他对象中,通常称为看守者。看守者对纪念品的访问权限有限,因此它无法更改其状态。同样,发起者可以访问备忘录中的所有字段,允许它随意恢复其先前的状态。
在实现撤销时,命令和备忘录设计模式可以一起使用。命令负责对目标对象执行各种操作,而备忘录在命令执行之前记录该对象的状态。
UML 类图
实施步骤
源代码实现
需要时,Editor (Originator) 类可以创建其自身状态的快照以及从快照中恢复其状态。
Memento 是一个值对象,充当发起者当前状态的快照。使备忘录不可变并且只通过构造函数传递一次数据是很常见的。
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;}}}
除了知道何时以及为什么要捕获发起者的状态之外,看守者还知道何时恢复它。 看守者可以通过存储纪念品来跟踪发起者的历史。 当发起者必须及时返回时,看守者从堆栈中检索最顶部的 memento 并将其传递给发起者的恢复方法。
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 类嵌套在这个实现中的 originator 类中。发起者可以访问备忘录的字段和方法,即使它们已被声明为私有。尽管如此,看守者对纪念品的字段和方法的访问非常有限,这允许它在不改变其状态的情况下将纪念品存储在堆栈上。
何时应用 Memento 设计模式
- 当您需要创建对象状态的快照以便能够将其恢复到以前的状态时,请使用 Memento 模式。
- 每当直接访问对象的字段违反其封装时,请使用此模式。 Memento 使对象本身负责拍摄其状态的快照。快照不能被任何其他对象读取,因此原始对象的状态数据是安全可靠的。
Memento 设计模式的优点
- 可以在不违反其封装的情况下创建对象状态的快照。
- 让看守者维护发起者状态的历史,以便简化发起者的代码。
如果你喜欢这篇文章,你知道该怎么做
关注七爪网,获取更多APP/小程序/网站源码资源!