前言
有時候,我們真希望人生能有“Ctrl+Z”。在日常生活中,我們經常使用“撤銷”功能,例如在寫 Word、畫圖、寫代碼時一不小心操作失誤,就希望能回到之前的狀態。這種**“狀態快照 + 恢復”**機制,在設計模式中就叫做:備忘錄模式(Memento Pattern)。
本文將通過一個現實場景——寫小說與撤銷編輯操作,來貫穿講解備忘錄模式,并用 Java 代碼實現一套完整的例子,讓你在概念與實踐中都收獲滿滿。
一、備忘錄模式是什么
定義:在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,以便以后將其恢復。
換句話說,它是實現“撤銷(Undo)”、“回退(Rollback)”、“恢復(Recover)”的核心設計模式。
二、小說家與“撤銷按鈕”
想象你是個小說作者,每天都在寫小說的草稿。
- 每寫一段內容,你都可以“保存一下”(打個草稿快照);
- 寫崩了?你就點擊“撤銷”,恢復之前保存的狀態;
- 每一個“保存”其實就是一個備忘錄對象(Memento);
- 你就是“發起者(Originator)”;
- 草稿箱(Caretaker)里存著所有的“保存記錄”。
三、核心結構類比
角色名 | 類比于現實 | 作用說明 |
---|---|---|
Originator | 小說作者 | 擁有真實內容和狀態,可以保存或恢復 |
Memento | 每一段草稿 | 狀態快照,保存了當前文本 |
Caretaker | 草稿箱 | 保存多個草稿,但不關心內容細節 |
四、Java 實例
如下以java來實現:小說草稿的撤銷與恢復
Memento:草稿快照
// 草稿狀態(備忘錄)
public class DraftMemento {private final String content;public DraftMemento(String content) {this.content = content;}public String getContent() {return content;}
}
Originator:小說作者
// 發起者,寫小說的作者
public class Author {private String content;public void write(String text) {this.content = text;System.out.println("🖊? 當前寫作內容:" + content);}public DraftMemento saveDraft() {System.out.println("📁 保存草稿");return new DraftMemento(content);}public void restoreDraft(DraftMemento memento) {this.content = memento.getContent();System.out.println("?? 恢復到草稿:" + content);}public String getContent() {return content;}
}
Caretaker:草稿箱
import java.util.Stack;// 草稿箱,負責保存多個版本
public class DraftCaretaker {private final Stack<DraftMemento> drafts = new Stack<>();public void save(DraftMemento draft) {drafts.push(draft);}public DraftMemento undo() {if (!drafts.isEmpty()) {return drafts.pop();}return null;}public boolean hasHistory() {return !drafts.isEmpty();}
}
測試主類:模擬寫作與撤銷
public class MementoDemo {public static void main(String[] args) {Author author = new Author();DraftCaretaker caretaker = new DraftCaretaker();author.write("故事的開始:從前有座山");caretaker.save(author.saveDraft());author.write("故事第二章:山里有個廟");caretaker.save(author.saveDraft());author.write("故事第三章:廟里住著老和尚和小和尚");System.out.println("\n?? 寫崩了!撤銷!");if (caretaker.hasHistory()) {author.restoreDraft(caretaker.undo()); // 撤銷到第二章}System.out.println("\n📄 當前內容:" + author.getContent());}
}
輸出示例
🖊? 當前寫作內容:故事的開始:從前有座山
📁 保存草稿
🖊? 當前寫作內容:故事第二章:山里有個廟
📁 保存草稿
🖊? 當前寫作內容:故事第三章:廟里住著老和尚和小和尚?? 寫崩了!撤銷!
?? 恢復到草稿:故事第二章:山里有個廟📄 當前內容:故事第二章:山里有個廟
五、UML圖
@startuml
title 備忘錄模式(Memento Pattern)UML 類圖class Author {- content: String+ write(text: String): void+ saveDraft(): DraftMemento+ restoreDraft(memento: DraftMemento): void+ getContent(): String
}class DraftMemento {- content: String+ getContent(): String
}class DraftCaretaker {- drafts: Stack<DraftMemento>+ save(m: DraftMemento): void+ undo(): DraftMemento+ hasHistory(): boolean
}Author --> DraftMemento : 創建備份
DraftCaretaker --> DraftMemento : 保存 & 提取
Author --> DraftCaretaker : 恢復狀態
六、適用場景一覽
場景 | 描述 |
---|---|
編輯器的撤銷重做 | 文本編輯、畫圖軟件 |
游戲存檔系統 | 保存玩家狀態,失敗后恢復 |
數據庫事務回滾 | 恢復操作前的狀態 |
配置修改回滾 | 系統設置、軟件參數 |
七、小結
優點:
- 不破壞封裝性,內部狀態對外透明
- 實現撤銷/恢復簡單靈活
- 多狀態歷史管理方便
注意事項:
- 每次保存都會占用內存,可能引起性能開銷
- 狀態對象較大時,建議存儲增量而非全量
備忘錄模式就像我們生活中的“后悔藥”,
它幫你把時間打包,瓶中封印,
等你想“回到昨天”,只需輕輕一喚。
八、參考
《23種設計模式概覽》