設計模式——備忘錄設計模式(行為型)

摘要

備忘錄設計模式是一種行為型設計模式,用于在不破壞封裝性的前提下,捕獲對象的內部狀態并在需要時恢復。它包含三個關鍵角色:原發器(Originator)、備忘錄(Memento)和負責人(Caretaker)。該模式的優點包括保留對象狀態、支持回滾和易于實現撤銷/重做功能,但缺點是狀態快照可能占用大量內存且管理復雜。其結構可通過嵌套類或中間接口類圖表示,實現方式涉及原發器創建和恢復備忘錄、備忘錄存儲狀態、負責人保存和獲取備忘錄。適合用于需要撤銷/重做功能或頻繁保存狀態的場景,但當狀態變化不頻繁或內存受限時則不適合。實戰示例包括數據庫腳本、實體類和狀態保存與撤銷API。此外,該模式可與其他設計模式或技術結合使用,如狀態機模式、命令模式、責任鏈模式、原型模式、觀察者模式、策略模式、持久化機制和Spring AOP/注解。

1. 備忘錄設計模式定義

備忘錄設計模式(Memento Pattern) 是一種行為型設計模式,用于在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在以后需要時將其恢復到原先的狀態。

1.1. ? 關鍵角色

角色

說明

Originator

原發器,擁有內部狀態,需要保存快照并恢復自身狀態

Memento

備忘錄,存儲 Originator的內部狀態,通常是一個不可變對象

Caretaker

負責人,管理備忘錄的保存與恢復,但不訪問其內容

1.2. 優點:

  • 保留對象狀態,支持回滾
  • 不破壞封裝(狀態通過 Memento 管理)
  • 易于實現撤銷/重做功能

1.3. 缺點:

  • 狀態快照可能占用大量內存
  • Caretaker 可能需要管理多個 Memento,帶來管理復雜性

2. 備忘錄設計模式結構

2.1. 基于嵌套類類圖

2.2. 基于中間接口類圖

2.3. 備忘錄時序圖

3. 備忘錄設計模式實現方式

備忘錄設計模式(Memento Pattern)實現方式的核心在于:在不破壞對象封裝性的前提下,捕獲并保存對象的內部狀態,以便之后可以將其恢復。

3.1. 🧩 Originator(原發器):擁有狀態,負責創建和恢復備忘錄

public class Originator {private String state; // 需要保存的內部狀態public void setState(String state) {this.state = state;System.out.println("設置狀態為:" + state);}public String getState() {return state;}// 創建備忘錄public Memento saveStateToMemento() {return new Memento(state);}// 從備忘錄恢復狀態public void restoreStateFromMemento(Memento memento) {this.state = memento.getState();System.out.println("恢復狀態為:" + state);}
}

3.2. 🧩 Memento(備忘錄):只暴露給 Originator,用于存儲狀態

public class Memento {private final String state;public Memento(String state) {this.state = state;}// 只有 Originator 使用protected String getState() {return state;}
}

3.3. 🧩 Caretaker(管理者):負責保存、獲取備忘錄,但不操作內容

import java.util.ArrayList;
import java.util.List;public class Caretaker {private List<Memento> mementoList = new ArrayList<>();// 添加備忘錄public void add(Memento memento) {mementoList.add(memento);}// 獲取某個備忘錄public Memento get(int index) {return mementoList.get(index);}
}

3.4. 🚀 使用示例(模擬狀態保存與恢復)

public class Client {public static void main(String[] args) {Originator originator = new Originator();Caretaker caretaker = new Caretaker();originator.setState("狀態 #1");originator.setState("狀態 #2");caretaker.add(originator.saveStateToMemento()); // 保存狀態2originator.setState("狀態 #3");caretaker.add(originator.saveStateToMemento()); // 保存狀態3originator.setState("狀態 #4");// 恢復狀態originator.restoreStateFromMemento(caretaker.get(0)); // 恢復到狀態2originator.restoreStateFromMemento(caretaker.get(1)); // 恢復到狀態3}
}

3.5. ? 備忘錄總結

組件

職責

Originator

負責創建和恢復備忘錄

Memento

存儲狀態(不可變對象)

Caretaker

管理多個備忘錄(可以是棧、隊列、列表)

3.6. ? 延伸使用(如 Spring 項目中)

你可以將備忘錄模式與:

  • Spring 注解(如 @Service@Component
  • 數據持久化(備忘錄入庫,支持長期存檔)
  • REST 接口撤銷(如風控規則撤銷)
  • 狀態機結合(狀態保存+回滾)

4. 備忘錄設計模式適合場景

4.1. ? 適合使用備忘錄模式的場景

場景

說明

? 撤銷操作(Undo/Redo)功能

比如文本編輯器、IDE、畫圖工具,用戶希望能夠一步步撤銷操作。

? 狀態快照與回滾

比如事務性系統、工作流、審批流、游戲存檔等,可以保存當前狀態,失敗時快速回滾。

? 風控策略、配置管理

在金融風控系統中,策略配置改動后,能夠恢復到某個時間點前的策略狀態。

? 狀態機狀態保存

和狀態機結合使用,記錄每個狀態變化的歷史,可實現狀態追溯。

? 臨時修改但可還原的場景

比如購物車中的臨時優惠應用、試算試驗。

? AI / 數據建模模擬

在做一系列模擬實驗時,需要保存中間狀態,方便比較或回退。

4.2. ? 不適合使用備忘錄模式的場景

場景

原因

? 狀態對象非常龐大或頻繁變動

會頻繁創建大量備份,造成內存/存儲負擔。例如大型圖像、視頻編輯。

? 備份數據無法或不允許暴露給外部

即使模式保證封裝性,有些敏感狀態如密鑰/隱私也不應被存儲。

? 狀態之間無明顯斷點或快照意義不大

比如高頻實時流系統,數據快速變化,快照意義小。

? 備份狀態對業務無價值

若狀態回滾從不發生,記錄也無實際意義,反而增加維護成本。

? 替代機制更適合

比如用數據庫的事務機制或版本控制系統就能解決的,不必使用設計模式實現復雜備份。

總結:備忘錄模式適用于“狀態可保存且可能需要還原”的對象,在撤銷、版本控制、配置管理等場景特別合適。

5. 備忘錄設計模式實戰示例

在風控系統中,用戶可配置規則,每次變更都自動保存歷史狀態,并支持「撤銷」功能。

5.1. ?? 數據庫腳本(rule_history.sql

CREATE TABLE rule_config (id BIGINT PRIMARY KEY AUTO_INCREMENT,rule_code VARCHAR(64),rule_content TEXT,version INT,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE rule_memento (id BIGINT PRIMARY KEY AUTO_INCREMENT,rule_id BIGINT,rule_content TEXT,version INT,saved_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

5.2. ?? 實體類

@Data
@Entity
@Table(name = "rule_config")
public class RuleConfig {@Id@GeneratedValueprivate Long id;private String ruleCode;private String ruleContent;private Integer version;
}
@Data
@Entity
@Table(name = "rule_memento")
public class RuleMemento {@Id@GeneratedValueprivate Long id;private Long ruleId;private String ruleContent;private Integer version;private Timestamp savedAt;
}

5.3. 🧠 備忘錄模式結構

5.3.1. 備忘錄(Memento)

public class RuleMementoSnapshot {private final String content;private final Integer version;public RuleMementoSnapshot(String content, Integer version) {this.content = content;this.version = version;}public String getContent() {return content;}public Integer getVersion() {return version;}
}

5.3.2. 發起人(Originator)

@Component
public class RuleOriginator {public RuleMementoSnapshot save(RuleConfig ruleConfig) {return new RuleMementoSnapshot(ruleConfig.getRuleContent(), ruleConfig.getVersion());}public void restore(RuleConfig ruleConfig, RuleMementoSnapshot snapshot) {ruleConfig.setRuleContent(snapshot.getContent());ruleConfig.setVersion(snapshot.getVersion());}
}

5.3.3. 負責人(Caretaker)

@Service
public class RuleHistoryService {@Autowiredprivate RuleMementoRepository mementoRepository;public void saveMemento(Long ruleId, RuleMementoSnapshot snapshot) {RuleMemento memento = new RuleMemento();memento.setRuleId(ruleId);memento.setRuleContent(snapshot.getContent());memento.setVersion(snapshot.getVersion());mementoRepository.save(memento);}public RuleMementoSnapshot getLastMemento(Long ruleId) {RuleMemento latest = mementoRepository.findTopByRuleIdOrderBySavedAtDesc(ruleId);return new RuleMementoSnapshot(latest.getRuleContent(), latest.getVersion());}
}

5.4. 💡 狀態保存與撤銷 API(Controller)

@RestController
@RequestMapping("/api/rule")
public class RuleController {@Autowired private RuleRepository ruleRepository;@Autowired private RuleHistoryService historyService;@Autowired private RuleOriginator originator;@PostMapping("/update")public ResponseEntity<String> updateRule(@RequestBody RuleConfig newRule) {RuleConfig old = ruleRepository.findById(newRule.getId()).orElseThrow();RuleMementoSnapshot snapshot = originator.save(old);historyService.saveMemento(old.getId(), snapshot);old.setRuleContent(newRule.getRuleContent());old.setVersion(old.getVersion() + 1);ruleRepository.save(old);return ResponseEntity.ok("規則已更新并記錄歷史");}@PostMapping("/undo/{ruleId}")public ResponseEntity<String> undo(@PathVariable Long ruleId) {RuleConfig rule = ruleRepository.findById(ruleId).orElseThrow();RuleMementoSnapshot lastSnapshot = historyService.getLastMemento(ruleId);originator.restore(rule, lastSnapshot);ruleRepository.save(rule);return ResponseEntity.ok("回滾成功");}
}

6. 備忘錄設計模式思考

備忘錄設計模式(Memento Pattern)在實際開發中往往不會單獨使用,而是與其他設計模式組合,以解決更復雜的狀態管理、回滾、撤銷等需求。下面是常見的組合方式和典型應用場景,尤其適合 金融風控、流程引擎、配置管理 等場景。

6.1. ? 備忘錄模式 + 狀態機模式(State Pattern)

組合說明:

  • 狀態機負責管理狀態切換邏輯;
  • 備忘錄負責保存每個狀態快照,實現狀態回滾/撤銷

場景示例:

  • 風控審批流程中,支持將任務狀態退回上一步。
  • 訂單狀態(待支付 → 已支付 → 配送中)中用戶申請退款,需要回退到「待支付」。

6.2. ? 備忘錄模式 + 命令模式(Command Pattern)

組合說明:

  • 命令封裝用戶操作;
  • 備忘錄記錄執行前的狀態,實現 undo() 操作。

場景示例:

  • 金融交易撤銷;
  • 系統管理員修改風控規則,每次操作都可以回滾。

6.3. ? 備忘錄模式 + 責任鏈模式(Chain of Responsibility)

組合說明:

  • 每個處理節點執行任務前記錄狀態;
  • 執行失敗時利用備忘錄回滾上一步處理。

場景示例:

  • 信貸審批流程,每個環節出錯需恢復上一次狀態。

6.4. ? 備忘錄模式 + 原型模式(Prototype Pattern)

組合說明:

  • 使用原型的淺/深拷貝方式快速創建備忘錄對象;
  • 提高狀態快照的創建效率。

場景示例:

  • 在風險規則配置頁面修改參數時,使用深克隆構建快照并保存。

6.5. ? 備忘錄模式 + 觀察者模式(Observer Pattern)

組合說明:

  • 狀態變化時通知觀察者;
  • 備忘錄用于記錄狀態變化歷史。

場景示例:

  • 配置變更通知下游服務,但允許撤銷恢復上一個配置。

6.6. ? 備忘錄模式 + 策略模式(Strategy Pattern)

組合說明:

  • 策略負責計算/處理;
  • 備忘錄記錄策略執行前后的狀態,便于結果回退。

場景示例:

  • 多種風控策略組合決策后,若發現誤判,恢復上一次執行前的狀態。

6.7. ? 備忘錄模式 + 持久化機制(如 Repository、數據庫)

組合說明:

  • 備忘錄對象不是只存在于內存,而是持久化保存(如 JSON 存庫)。
  • 支持跨進程或長時間狀態恢復。

場景示例:

  • 某條風控規則上線后的歷史版本備份,可通過后臺 UI 操作恢復。

6.8. ? 備忘錄模式 + Spring AOP/注解

組合說明:

  • 使用注解(如 @MementoBackup)自動攔截方法;
  • 在方法前后生成并記錄狀態快照,實現零侵入式回滾機制

場景示例:

  • Spring Boot 應用中,支持風控參數調整回滾功能,只需加注解即可。

6.9. 🧩 組合參考表

組合模式

典型作用

適用系統類型

狀態機

管理狀態轉換+回滾

審批系統、風控流程引擎

命令

操作封裝+撤銷

配置平臺、策略決策平臺

責任鏈

多環節狀態保護

風控引擎、流程引擎

原型

快速復制狀態

配置回滾、草稿管理

觀察者

狀態廣播+恢復

分布式配置中心

策略

策略組合+狀態對比

風控策略、限額控制

持久化

狀態歷史存檔

長期任務跟蹤系統

AOP/注解

自動化攔截/回滾

企業級 Spring 應用

博文參考

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

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

相關文章

動態規劃十大經典題型狀態轉移、模版等整理(包括leetcode、洛谷題號)

動態規劃十大經典題目整理 0-1 背包問題&#xff08;0-1 Knapsack Problem&#xff09; LeetCode題號&#xff1a;無直接對應洛谷OJ題號&#xff1a;P1048狀態轉移方程&#xff1a;dp[j] max(dp[j], dp[j - weight[i]] value[i])C代碼模板&#xff1a; int dp[capacity 1…

簡單transformer運用

通俗易懂解讀&#xff1a;hw04.py 文件內容與 Transformer 的應用 這個文件是一個 Python 腳本&#xff08;hw04.py&#xff09;&#xff0c;用于完成 NTU 2021 Spring 機器學習課程的 HW4 作業任務&#xff1a;揚聲器分類&#xff08;Speaker Classification&#xff09;。它…

redis的哨兵模式和Redis cluster

目錄 一. redis的主從復制 二. 哨兵模式 2.1 定義 2.2 作用 2.3 配置實例 三. Redis cluster 3.1 定義 3.2 作用 3.3 配置實例 1. 新建集群文件目錄 2. 準備可執行文件到每個文件夾 3. 開啟群集功能 4. 啟動redis節點 5. 查看是否啟動成功 6. 啟動集群 7. 測試…

簡述八大排序(Sort)

1.插入排序 1.1直接插入排序 給定一組數據&#xff0c;若數據只有一個肯定是有序的&#xff0c;我們將無序數據一個個插入到已有序的數據中。用i遍歷無序數據&#xff0c;j遍歷有序數據&#xff0c;找到合適插入位置&#xff0c;用tmp存放目標插入數據&#xff0c;將其與j對應…

xcode 編譯運行錯誤 Sandbox: rsync(29343) deny(1) file-write-create

解決方法 方法一&#xff1a;修改Targets -> Build Settings 中 ENABLE_USER_SCRIPT_SANDBOXING 設置 NO 方法二&#xff1a;項目使用cocoaPods進行三方管理 且 使用了 use_frameworks&#xff0c;把 use_frameworks 注釋掉,然后重新自行pod install

linux系統中防火墻的操作

防火墻 開放ssh端口 sudo ufw allow 22/tcp # 允許 SSH 連接 sudo ufw enable開放防火墻端口 sudo ufw allow 80/tcp # HTTP sudo ufw allow 443/tcp # HTTPS&#xff08;如果需要&#xff09; sudo ufw enable查看擋墻防火墻設置 sudo ufw status刪除其中一條防火墻規…

[特殊字符] 超強 Web React版 PDF 閱讀器!支持分頁、縮放、旋轉、全屏、懶加載、縮略圖!

在現代 Web 項目中&#xff0c;PDF 瀏覽是一個常見需求&#xff1a;從政務公文到合同協議&#xff0c;PDF 文件無處不在。但很多方案要么體驗不佳&#xff0c;要么集成復雜。今天&#xff0c;我給大家帶來一個開箱即用、功能全面的 PDF 預覽組件 —— [PDFView](https://www.np…

設計模式——策略設計模式(行為型)

摘要 策略設計模式是一種行為型設計模式&#xff0c;它定義了一系列算法并將每個算法封裝起來&#xff0c;使它們可以相互替換。該模式讓算法的變化獨立于使用算法的客戶&#xff0c;從而使得算法可以靈活地切換和擴展。其主要角色包括策略接口、具體策略類和環境類。策略模式…

DeepSeek-R1-0528,官方的端午節特別獻禮

DeepSeek&#xff1a;端午安康&#xff01;刻在國人骨子里的浪漫 2025 年 05 月 28 日 | DeepSeek 端午特別獻禮 當粽葉飄香時&#xff0c;DeepSeek 悄然帶來一份節日驚喜 版本號 DeepSeek-R1-0528 正式上線 官方賦予它的靈魂是&#xff1a; 思考更深 推理更強 用戶通過官網…

mac安裝brew時macos無法信任ruby的解決方法

背景 在使用如下腳本安裝brew時&#xff0c;遇到安裝ruby&#xff0c;macos不信任外部軟件&#xff0c;在安全性點擊信任仍然無法安裝。 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"如何解決 本地安裝好符…

2025音頻傳輸模塊全球選購指南:高品質音頻體驗的品牌之選

隨著無線技術的迅猛發展&#xff0c;音頻傳輸模塊&#xff08;Audio Transmission Module&#xff09;已成為高品質音頻體驗的關鍵技術之一。它們廣泛應用于智能家居、無線耳機、會議系統、廣播設備以及專業音頻領域。面對市場上多樣化的產品&#xff0c;如何選擇適合自己需求的…

解析樓宇自控系統:分布式結構的核心特點與優勢展現

在建筑智能化發展的進程中&#xff0c;樓宇自控系統作為實現建筑高效運行與管理的關鍵&#xff0c;其系統結構的選擇至關重要。傳統的集中式樓宇自控系統在面對日益復雜的建筑環境和多樣化的管理需求時&#xff0c;逐漸暴露出諸多弊端&#xff0c;如可靠性低、擴展性差、響應速…

Spring Boot對一些技術框架進行了統一版本號管理

這個說法是 正確的。 Spring Boot 對許多常用依賴進行了版本管理&#xff0c;因此在項目中引入這些依賴時&#xff0c;通常不需要指定版本號。 Spring Boot 依賴版本管理 &#x1f6e0;? spring-boot-starter-parent&#xff1a;當你的項目在 pom.xml (Maven 項目) 中繼承自…

關于MySQL的索引

一、索引 1、索引概述 1.1、介紹 索引&#xff08; index &#xff09;是幫助 MySQL 高效獲取數據的數據結構 ( 有序 ) 。在數據之外&#xff0c;數據庫系統還維護著滿足特定查找算法的數據結構&#xff0c;這些數據結構以某種方式引用&#xff08;指向&#xff09;數據&…

微服務常用日志追蹤方案:Sleuth + Zipkin + ELK

在微服務架構中&#xff0c;一個用戶請求往往需要經過多個服務的協同處理。為了有效追蹤請求的完整調用鏈路&#xff0c;需要一套完整的日志追蹤方案。Sleuth Zipkin ELK 組合提供了完整的解決方案 Sleuth&#xff1a;生成和傳播追蹤IDZipkin&#xff1a;收集、存儲和可視化…

R語言基礎| 創建數據集

在R語言中&#xff0c;有多種數據類型&#xff0c;用以存儲和處理數據。每種數據類型都有其特定的用途和操作函數&#xff0c;使得R語言在處理各種數據分析任務時非常靈活和強大&#xff1a; 向量&#xff08;Vector&#xff09;: 向量是R語言中最基本的數據類型&#xff0c;它…

nssctf第二題[SWPUCTF 2021 新生賽]簡簡單單的邏輯

這是題目&#xff0c;下載后得到一個python文件,打開 解讀代碼&#xff1a; for i in range(len(list)):key (list[i]>>4)((list[i] & 0xf)<<4)result str(hex(ord(flag[i])^key))[2:].zfill(2)list[i]>>4&#xff1a;從列表中取數字同時高4位向右位…

mysql(十五)

目錄 子查詢 1.準備工作 2--創建表格 3--插入數據 2.where 子查詢單列單個數據 格式 查詢 3.where 子查詢單列多個數據(in) 格式 查詢 使用子查詢 4.from 多行多數據 格式 查詢 子查詢 將select的查詢的返回結果 當成另外一個selet語句的內容去使用。 子查詢放在()里面 注意…

【HarmonyOS 5】鴻蒙Taro跨端框架

?Taro跨端框架? 支持React語法開發鴻蒙應用&#xff0c;架構分為三層&#xff1a; ArkVM層運行業務代碼和React核心TaroElement樹處理節點創建和屬性綁定TaroRenderNode虛擬節點樹與上屏節點一一對應 import { Component } from tarojs/taro export default class MyCompon…

華為OD機試真題——會議接待 /代表團坐車(2025A卷:200分)Java/python/JavaScript/C++/C語言/GO六種最佳實現

2025 A卷 200分 題型 本文涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、測試用例以及綜合分析; 并提供Java、python、JavaScript、C++、C語言、GO六種語言的最佳實現方式! 本文收錄于專欄:《2025華為OD真題目錄+全流程解析/備考攻略/經驗分享》 華為OD機試真題《會議…