C++设计模式之备忘录模式:以家具生产为例
在家具生产车间里,实木沙发的生产过程常常需要经历木材裁切、打磨、上色、组装等多个环节。有时在某个环节完成后,客户可能临时要求调整风格,或者生产过程中出现质量问题需要回溯到之前的合格状态——如果能像给家具“拍快照”一样保存关键节点的状态,后续就能轻松恢复。这种“状态快照+回溯”的需求,恰好可以通过备忘录模式完美解决。本文将结合实木沙发生产场景,详解备忘录模式的原理,并通过C++代码实现落地。
一、备忘录模式核心原理
备忘录模式(Memento Pattern)是一种行为型设计模式,其核心思想是:在不破坏对象封装性的前提下,捕获对象的内部状态,并在该对象之外保存这个状态,以便后续需要时能将对象恢复到之前的某个状态。
1.1 核心角色与家具生产场景对应
备忘录模式包含三个核心角色,我们可以直接与实木沙发生产流程中的实体对应,理解起来更直观:
-
发起人(Originator):需要保存状态的核心对象,负责创建备忘录(保存当前状态)和从备忘录恢复状态。对应场景中的“实木沙发”——它包含木材种类、尺寸、打磨等级、上色颜色等内部状态,能主动保存自己的生产节点状态,也能根据备份恢复状态。
-
备忘录(Memento):用于存储发起人对象的内部状态,且仅能由发起人访问其内部数据(保证封装性)。对应场景中的“生产状态快照单”——上面记录着沙发在某个关键节点(如“裁切完成”“打磨合格”)的具体状态信息,只有生产车间(发起人)能填写和读取。
-
管理者(Caretaker):负责管理备忘录对象,仅存储备忘录但不操作其内部数据(避免破坏封装)。对应场景中的“生产档案管理员”——负责收集和保管所有沙发的“状态快照单”,需要时提供给车间,但不会修改快照单上的内容。
1.2 模式设计关键原则
1. 封装性优先:备忘录的状态数据只能由发起人读写,管理者仅做存储,不解析状态;2. 状态粒度可控:只保存关键节点的状态(如裁切后、打磨后),避免存储过多冗余数据;3. 回溯灵活:管理者可存储多个备忘录,支持恢复到任意历史状态。
二、C++代码实现:实木沙发生产场景
我们以“实木沙发生产”为具体场景,实现备忘录模式:需求是保存沙发“裁切完成”“打磨合格”“上色完成”三个关键节点的状态,支持客户要求调整时回溯到任意节点。
2.1 代码结构设计
按照三个核心角色拆分代码,采用类的封装性保证状态安全:
-
先定义备忘录类(Memento):存储沙发状态,构造函数由发起人友元访问;
-
定义发起人类(Sofa):即实木沙发,包含状态属性和“保存状态”“恢复状态”的方法;
-
定义管理者类(Caretaker):用容器存储多个备忘录,提供“添加快照”“获取快照”的接口;
-
主函数演示:模拟沙发生产流程,保存关键节点状态,模拟回溯场景。
2.2 完整代码实现
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
// 前向声明发起人类,让备忘录类能将其设为友元
class Sofa;
// 1. 备忘录类(Memento):对应“生产状态快照单”
class SofaMemento {
private:
// 快照存储的状态:木材种类、尺寸、打磨等级、上色颜色
string woodType;
string size;
int polishLevel; // 打磨等级:1-5级,5级最光滑
string color;
// 关键:私有构造函数,仅友元(发起人)可创建
SofaMemento(string wt, string s, int pl, string c)
: woodType(wt), size(s), polishLevel(pl), color(c) {}
// 提供给发起人访问状态的接口(仅发起人可调用)
string getWoodType() { return woodType; }
string getSize() { return size; }
int getPolishLevel() { return polishLevel; }
string getColor() { return color; }
// 声明发起人为友元,使其能访问私有构造和状态接口
friend class Sofa;
};
// 2. 发起人类(Originator):对应“实木沙发”
class Sofa {
private:
// 沙发的当前状态(与备忘录存储的状态一致)
string woodType;
string size;
int polishLevel;
string color;
public:
// 构造函数:初始化沙发初始状态(裁切前的基础信息)
Sofa(string wt, string s)
: woodType(wt), size(s), polishLevel(0), color("未上色") {}
// 模拟生产环节:裁切(仅初始化尺寸和木材,实际无状态变化)
void cut() {
cout << "=== 完成裁切:木材=" << woodType << ",尺寸=" << size << endl;
}
// 模拟生产环节:打磨(提升打磨等级)
void polish(int level) {
if (level > 0 && level <= 5) {
polishLevel = level;
cout << "=== 完成打磨:等级提升至" << polishLevel << "级" << endl;
} else {
cout << "打磨等级无效(需1-5级)" << endl;
}
}
// 模拟生产环节:上色(设置颜色)
void paint(string c) {
color = c;
cout << "=== 完成上色:颜色为" << color << endl;
}
// 核心方法1:创建备忘录(保存当前状态)
SofaMemento* saveStateToMemento() {
// 用当前状态创建备忘录(因是友元,可调用私有构造)
return new SofaMemento(woodType, size, polishLevel, color);
}
// 核心方法2:从备忘录恢复状态
void restoreStateFromMemento(SofaMemento* memento) {
// 从备忘录获取状态并覆盖当前状态
woodType = memento->getWoodType();
size = memento->getSize();
polishLevel = memento->getPolishLevel();
color = memento->getColor();
cout << "=== 已恢复状态:木材=" << woodType << ",尺寸=" << size
<< ",打磨等级=" << polishLevel << ",颜色=" << color << endl;
}
// 辅助方法:显示当前状态
void showCurrentState() {
cout << "当前沙发状态:木材=" << woodType << ",尺寸=" << size
<< ",打磨等级=" << polishLevel << ",颜色=" << color << endl;
}
};
// 3. 管理者类(Caretaker):对应“生产档案管理员”
class SofaCaretaker {
private:
// 用map存储多个备忘录,key为状态节点名称(如“裁切完成”),value为对应快照
map<string, SofaMemento*> mementoMap;
public:
// 存储备忘录(添加快照)
void addMemento(string stateName, SofaMemento* memento) {
mementoMap[stateName] = memento;
cout << "=== 已保存状态快照:" << stateName << endl;
}
// 获取备忘录(根据节点名称获取快照)
SofaMemento* getMemento(string stateName) {
if (mementoMap.find(stateName) != mementoMap.end()) {
return mementoMap[stateName];
}
cout << "未找到状态快照:" << stateName << endl;
return nullptr;
}
// 析构函数:释放备忘录内存(避免内存泄漏)
~SofaCaretaker() {
for (auto& pair : mementoMap) {
delete pair.second;
}
mementoMap.clear();
}
};
// 主函数:模拟沙发生产流程与状态回溯
int main() {
// 1. 初始化:创建一款北美黑胡桃木、180cm尺寸的沙发
Sofa sofa("北美黑胡桃木", "180cm*90cm*85cm");
// 2. 创建管理员(档案员)
SofaCaretaker caretaker;
// 3. 生产流程:裁切 → 保存“裁切完成”快照
sofa.cut();
caretaker.addMemento("裁切完成", sofa.saveStateToMemento());
sofa.showCurrentState();
cout << "-------------------------" << endl;
// 4. 生产流程:打磨至3级 → 保存“打磨合格”快照
sofa.polish(3);
caretaker.addMemento("打磨合格", sofa.saveStateToMemento());
sofa.showCurrentState();
cout << "-------------------------" << endl;
// 5. 生产流程:上色为深棕色 → 保存“上色完成”快照
sofa.paint("深棕色");
caretaker.addMemento("上色完成", sofa.saveStateToMemento());
sofa.showCurrentState();
cout << "-------------------------" << endl;
// 6. 场景1:客户临时要求改为“浅橡木色”,但后续反悔要恢复到“打磨合格”状态
sofa.paint("浅橡木色");
cout << "客户要求修改后:";
sofa.showCurrentState();
sofa.restoreStateFromMemento(caretaker.getMemento("打磨合格"));
cout << "-------------------------" << endl;
// 7. 场景2:恢复后重新上色为“胡桃木本色”,并保存新快照
sofa.paint("胡桃木本色");
caretaker.addMemento("最终成品", sofa.saveStateToMemento());
sofa.showCurrentState();
return 0;
}
}
2.3 代码解析
-
封装性保障:备忘录的构造函数和状态获取接口均为私有,仅发起人(Sofa类)可访问,管理者(SofaCaretaker)只能存储和传递备忘录,无法修改或解析状态,避免了状态被误操作。
-
状态快照管理:管理者用map存储多个快照,通过“状态节点名称”(如“打磨合格”)快速定位,支持灵活回溯到任意历史节点。
-
生产流程适配:代码中的cut()、polish()、paint()方法模拟了实际生产环节,saveStateToMemento()和restoreStateFromMemento()方法无缝嵌入流程,符合家具生产的实际需求。
三、备忘录模式的应用价值与扩展
3.1 家具生产场景的实际价值
1. 质量回溯:若上色后发现木材有裂纹,可快速恢复到“打磨合格”状态,检查打磨环节是否存在问题;2. 客户需求变更适配:客户临时修改风格时,无需重新从裁切开始,直接回溯到对应节点调整;3. 版本管理:保存多个“成品快照”,方便客户对比不同版本的效果。
3.2 模式扩展方向
-
快照压缩:若状态数据量大(如复杂家具的雕花细节),可在备忘录中对状态进行压缩存储,减少内存占用。
-
持久化存储:将管理者中的备忘录写入文件或数据库,实现跨程序重启的状态回溯(如生产中断后恢复)。
-
快照清理:增加快照过期机制,自动删除长期未使用的历史快照,避免存储冗余。
四、总结
备忘录模式通过“发起人-备忘录-管理者”的三元结构,巧妙解决了“对象状态快照与回溯”的问题,核心优势在于不破坏封装性的前提下实现状态管理。结合家具生产场景的代码实现可见,该模式能完美适配实际业务流程,让状态回溯变得简单、安全且灵活。在C++开发中,当需要对对象状态进行版本控制或回溯时,备忘录模式无疑是最优选择之一。
更多推荐


所有评论(0)