设计模式——备忘录设计模式(行为型)
摘要
备忘录设计模式是一种行为型设计模式,用于在不破坏封装性的前提下,捕获对象的内部状态并在需要时恢复。它包含三个关键角色:原发器(Originator)、备忘录(Memento)和负责人(Caretaker)。该模式的优点包括保留对象状态、支持回滚和易于实现撤销/重做功能,但缺点是状态快照可能占用大量内存且管理复杂。其结构可通过嵌套类或中间接口类图表示,实现方式涉及原发器创建和恢复备忘录、备忘录存储状态、负责人保存和获取备忘录。适合用于需要撤销/重做功能或频繁保存状态的场景,但当状态变化不频繁或内存受限时则不适合。实战示例包括数据库脚本、实体类和状态保存与撤销API。此外,该模式可与其他设计模式或技术结合使用,如状态机模式、命令模式、责任链模式、原型模式、观察者模式、策略模式、持久化机制和Spring AOP/注解。
1. 备忘录设计模式定义
备忘录设计模式(Memento Pattern) 是一种行为型设计模式,用于在不破坏封装性的前提下,捕获一个对象的内部状态,并在以后需要时将其恢复到原先的状态。
1.1. ✅ 关键角色
角色 | 说明 |
Originator | 原发器,拥有内部状态,需要保存快照并恢复自身状态 |
Memento | 备忘录,存储 |
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 应用 |