备忘录模式是一种行为设计模式,它允许在不破坏封装的情况下捕获和恢复对象的内部状态。这种模式在需要实现撤销/重做功能,或者需要保存对象状态快照的场景中特别有用。
// 备忘录类
class TextEditorMemento {
#content;
#cursorPosition;
constructor(content, cursorPosition) {
this.#content = content;
this.#cursorPosition = cursorPosition;
}
getContent() {
return this.#content;
}
getCursorPosition() {
return this.#cursorPosition;
}
}
// 原发器类
class TextEditor {
#content = '';
#cursorPosition = 0;
type(text) {
this.#content = this.#content.slice(0, this.#cursorPosition) + text + this.#content.slice(this.#cursorPosition);
this.#cursorPosition += text.length;
console.log(`Typed: "${text}". Current content: "${this.#content}"`);
}
moveCursor(position) {
if (position >= 0 && position <= this.#content.length) {
this.#cursorPosition = position;
console.log(`Cursor moved to position ${position}`);
} else {
console.log("Invalid cursor position");
}
}
delete(length) {
if (this.#cursorPosition + length <= this.#content.length) {
this.#content = this.#content.slice(0, this.#cursorPosition) + this.#content.slice(this.#cursorPosition + length);
console.log(`Deleted ${length} character(s). Current content: "${this.#content}"`);
} else {
console.log("Cannot delete: not enough characters after cursor");
}
}
save() {
return new TextEditorMemento(this.#content, this.#cursorPosition);
}
restore(memento) {
this.#content = memento.getContent();
this.#cursorPosition = memento.getCursorPosition();
console.log(`State restored. Current content: "${this.#content}", Cursor at: ${this.#cursorPosition}`);
}
getContent() {
return this.#content;
}
}
// 负责人类
class History {
#mementos = [];
#currentIndex = -1;
push(memento) {
this.#mementos.splice(this.#currentIndex + 1);
this.#mementos.push(memento);
this.#currentIndex++;
}
undo() {
if (this.#currentIndex > 0) {
this.#currentIndex--;
return this.#mementos[this.#currentIndex];
}
return null;
}
redo() {
if (this.#currentIndex < this.#mementos.length - 1) {
this.#currentIndex++;
return this.#mementos[this.#currentIndex];
}
return null;
}
}
// 客户端代码
function demonstrateMemento() {
const editor = new TextEditor();
const history = new History();
// 初始操作
editor.type("Hello ");
history.push(editor.save());
editor.type("world");
history.push(editor.save());
editor.type("!");
history.push(editor.save());
// 撤销操作
console.log("\nUndo operation:");
let undoMemento = history.undo();
if (undoMemento) editor.restore(undoMemento);
console.log("\nUndo operation:");
undoMemento = history.undo();
if (undoMemento) editor.restore(undoMemento);
// 重做操作
console.log("\nRedo operation:");
let redoMemento = history.redo();
if (redoMemento) editor.restore(redoMemento);
// 新的操作
console.log("\nNew operation:");
editor.moveCursor(5);
editor.type("beautiful ");
history.push(editor.save());
console.log("\nFinal content:");
console.log(editor.getContent());
}
demonstrateMemento();
实现思路
-
TextEditorMemento类(备忘录):- 存储文本编辑器的状态(内容和光标位置)。
- 使用私有字段来保护状态,只通过方法访问。
-
TextEditor类(原发器):- 包含文本编辑的核心功能(输入、删除、移动光标)。
- 实现
save方法创建当前状态的备忘录。 - 实现
restore方法从备忘录恢复状态。
-
History类(负责人):- 管理备忘录对象。
- 实现撤销(undo)和重做(redo)功能。
- 使用数组和索引来跟踪备忘录历史。
优点
- 封装性:原发器的内部状态被封装在备忘录中,不暴露给其他对象。
- 简化原发器:将存储状态的职责转移到备忘录对象,简化了原发器。
- 定义窄接口和宽接口:备忘录可以对原发器暴露宽接口,对其他对象暴露窄接口。
- 易于实现撤销/重做:通过管理备忘录的历史,可以轻松实现多步撤销和重做。