Java 編程之備忘錄模式

前言

有時候,我們真希望人生能有“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種設計模式概覽》
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87089.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87089.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87089.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

yolov13+bytetrack的目標跟蹤實現

目錄 1. 介紹 2. 相關工作 (Related Works) 3. 方法 (Method) 4. 統計和結果 5. 技術實現 ByteTrack: Multi-Object Tracking by Associating Every Detection Box 1. Motivation 2. BYTE 3. ByteTrack 具體代碼 UI界面設計 歷史記錄 完整代碼實現UI界面 1. 介紹 …

GO類型轉換與斷言面試題及參考答案

Go 中類型轉換與類型斷言的區別是什么? 在Go語言里,類型轉換和類型斷言是兩個不同的概念,它們在應用場景、語法格式以及底層實現上都存在明顯差異。 類型轉換主要用于將一種數據類型轉變為另一種數據類型,一般適用于基本數據類型之間的轉換,像整數與浮點數、字符串與字節…

【力扣 中等 C】79. 單詞搜索

目錄 題目 解法一&#xff1a;回溯 題目 解法一&#xff1a;回溯 void swap(char* a, char* b) {char tmp *a;*a *b;*b tmp; }void reverse(char* str) {int start 0, end strlen(str) - 1;while (start < end) {swap(&str[start], &str[end--]);} }bool se…

【數據標注師】分類標注

目錄 一、 **分類標注的認知底層邏輯**1. **三大核心挑戰2. **四維評估標準** 二、 **五階成長體系**? **階段1&#xff1a;分類體系深度內化&#xff08;2-4周&#xff09;**? **階段2&#xff1a;標注決策流程固化**? **階段3&#xff1a;場景化標注策略**? **階段4&…

大數據時代UI前端的智能化轉型策略:以用戶為中心的設計思維

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩! 一、引言&#xff1a;大數據驅動的 UI 前端變革浪潮 在數字化體驗競爭白熱化的今天&#xff…

【python實用小腳本-122】Detect Gender Webcam:基于Python和Keras的實時性別檢測工具

在計算機視覺和人工智能領域&#xff0c;實時性別檢測是一個具有廣泛應用前景的技術。從安防監控到智能廣告&#xff0c;性別檢測可以幫助系統更好地理解和響應用戶需求。為了實現這一功能&#xff0c;我們開發了一個基于Python和Keras的實時性別檢測工具——detect_gender_web…

Redis4

Redis除了緩存&#xff0c;還有哪些應用? Redis實現消息隊列 **使用Pub/Sub模式&#xff1a;**Redis的Pub/Sub是一種基于發布/訂閱的消息模式&#xff0c;任何客戶端都可以訂閱一個或多個頻道&#xff0c;發布者可以向特定頻道發送消息&#xff0c;所有訂閱該頻道的客戶端都會…

LEFE-Net:一種軸承故障診斷的輕量化高效特征提取網絡

一、研究背景與挑戰 軸承作為旋轉機械的核心部件&#xff0c;其健康狀態直接影響設備運行的安全性和可靠性。傳統的故障診斷方法&#xff08;如振動分析、油液檢測&#xff09;依賴人工經驗&#xff0c;效率低且易受主觀因素影響。近年來&#xff0c;基于深度學習的數據驅動方…

springboot+Apache POI 寫共導入導出

SpringBoot Apache POI 實現數據導入導出 功能特點&#xff1a; 智能列匹配&#xff1a; 支持精確列名匹配 支持忽略大小寫的列名匹配 自動匹配字段名&#xff08;當未指定ExcelProperty時&#xff09; 強大的類型轉換&#xff1a; 支持基本數據類型&#xff08;Integer/Lon…

Games101 Lecture3,Lecture4

旋轉矩陣邏輯推導 齊次坐標&#xff0c;解決平移的特殊情況 引入一個維度&#xff08;無物理意義&#xff1f;&#xff09;&#xff0c;輔助表達平移&#xff0c;為零時&#xff0c;表示向量&#xff0c;不為零時&#xff0c;表示點&#xff08;/w&#xff09; 三維旋轉矩陣 相…

折線圖多數據處理

前言&#xff1a; skline1有年份和新申請單位數&#xff0c;skline2有年份和有效期內單位數&#xff0c;我想要把1和2的年份放在一起從小到大放&#xff0c;沒有重復的&#xff0c;新申請單位數和有效期內單位數和年份的排列順序一致 實現&#xff1a; // 獲取原始數據 List…

documents4j導出pdf

一、前言 上一篇我們介紹了導出word&#xff0c;既然有了導出word&#xff0c;那么到處pdf也將會出現&#xff0c;導出word和pdf基本上是配套的需求&#xff0c;跑不了&#xff0c;那么本次我就簡單介紹一下導出pdf。 二、代碼實現 2.1、依賴引入 導出pdf是基于documents4j實現…

從零到一體驗 Qwen-TTS:用四川話合成語音的全流程技術實錄

今天很高興看到Qwen-TTS開源。試一試四川方言&#xff08;大概是成都版&#xff09;效果如何。本人無法判斷、有興趣的伙伴可以幫忙聽一聽。 四川方言TTS "胖娃胖嘟嘟&#xff0c;騎馬上成都&#xff0c;成都又好耍。胖娃騎白馬&#xff0c;白馬跳得高。胖娃耍關刀&…

php數據導出pdf文件

一.導出pdf文件&#xff0c;首先要安裝相關的類庫文件&#xff0c;我用的是dompdf類庫。 1.安裝類庫文件&#xff1a; composer require dompdf/dompdf 2.引入類庫文件到你的控制器中&#xff0c;創建方法&#xff1a; public function generatePdf(){//你需要打印的查詢內容…

Beam2.61.0版本消費kafka重復問題排查

1.問題出現過程 在測試環境測試flink的job的任務消費kafka的情況&#xff0c;通過往job任務發送一條消息&#xff0c;然后flink web ui上消費出現了兩條。然后通過重啟JobManager和TaskManager后&#xff0c;任務從checkpoint恢復后就會出現重復消費。當任務不從checkpoint恢復…

關于 java:9. Java 網絡編程

一、Socket 編程 Socket&#xff08;套接字&#xff09;是網絡通信的端點&#xff0c;是對 TCP/IP 協議的編程抽象&#xff0c;用于實現兩臺主機間的數據交換。 通俗來說&#xff1a; 可以把 Socket 理解為“電話插口”&#xff0c;插上后客戶端和服務端才能“通話”。 Sock…

主流零信任安全產品深度介紹

騰訊 iOA 零信任安全管理系統 功能&#xff1a;提供零信任接入、終端安全、數據防泄密等十余種功能模塊。可實現基于身份的動態訪問控制、終端安全一體化防護、數據防泄密體系等。核心優勢&#xff1a;基于騰訊內部千萬級終端實踐打磨&#xff0c;沉淀豐富場景方案&#xff0c…

LabVIEW裝配車體撓度無線測量

針對軌道交通車輛裝配過程中車體撓度測量需求&#xff0c;基于LabVIEW開發無線快速測量系統&#xff0c;采用品牌硬件構建高精度數據采集與傳輸架構。系統通過 ZigBee 無線傳輸技術、高精度模數轉換模塊及激光位移傳感器&#xff0c;實現裝配車體撓度的實時、自動、非接觸測量&…

java微服務-linux單機CPU接近100%優化

你這個場景&#xff1a; 4核16G 機器 同時運行了 8個 Spring Boot 微服務&#xff0c;每個 JAR 文件 100多 MB 導致 CPU 接近100% 確實是一個常見但資源緊繃的部署情境。下面是分層的優化建議&#xff0c;包括 JVM、系統、服務架構等多個方面&#xff0c;幫助你 降 CPU、穩…

MySQL表的約束和基本查詢

一.表的約束 1.1空屬性 當我們填寫問卷的時候,經常會有不允許為空的問題,比如電話號,姓名等等.而mysql上我們可以在創建表的時候,如果想要某一列不允許為空,可以加上not null來加以限制: mysql> create table myclass( -> class_name varchar(20) not null, -> cla…