在家具生产车间里,实木沙发的生产过程常常需要经历木材裁切、打磨、上色、组装等多个环节。有时在某个环节完成后,客户可能临时要求调整风格,或者生产过程中出现质量问题需要回溯到之前的合格状态——如果能像给家具“拍快照”一样保存关键节点的状态,后续就能轻松恢复。这种“状态快照+回溯”的需求,恰好可以通过备忘录模式完美解决。本文将结合实木沙发生产场景,详解备忘录模式的原理,并通过C++代码实现落地。

一、备忘录模式核心原理

备忘录模式(Memento Pattern)是一种行为型设计模式,其核心思想是:在不破坏对象封装性的前提下,捕获对象的内部状态,并在该对象之外保存这个状态,以便后续需要时能将对象恢复到之前的某个状态。

1.1 核心角色与家具生产场景对应

备忘录模式包含三个核心角色,我们可以直接与实木沙发生产流程中的实体对应,理解起来更直观:

  • 发起人(Originator):需要保存状态的核心对象,负责创建备忘录(保存当前状态)和从备忘录恢复状态。对应场景中的“实木沙发”——它包含木材种类、尺寸、打磨等级、上色颜色等内部状态,能主动保存自己的生产节点状态,也能根据备份恢复状态。

  • 备忘录(Memento):用于存储发起人对象的内部状态,且仅能由发起人访问其内部数据(保证封装性)。对应场景中的“生产状态快照单”——上面记录着沙发在某个关键节点(如“裁切完成”“打磨合格”)的具体状态信息,只有生产车间(发起人)能填写和读取。

  • 管理者(Caretaker):负责管理备忘录对象,仅存储备忘录但不操作其内部数据(避免破坏封装)。对应场景中的“生产档案管理员”——负责收集和保管所有沙发的“状态快照单”,需要时提供给车间,但不会修改快照单上的内容。

1.2 模式设计关键原则

1. 封装性优先:备忘录的状态数据只能由发起人读写,管理者仅做存储,不解析状态;2. 状态粒度可控:只保存关键节点的状态(如裁切后、打磨后),避免存储过多冗余数据;3. 回溯灵活:管理者可存储多个备忘录,支持恢复到任意历史状态。

二、C++代码实现:实木沙发生产场景

我们以“实木沙发生产”为具体场景,实现备忘录模式:需求是保存沙发“裁切完成”“打磨合格”“上色完成”三个关键节点的状态,支持客户要求调整时回溯到任意节点。

2.1 代码结构设计

按照三个核心角色拆分代码,采用类的封装性保证状态安全:

  1. 先定义备忘录类(Memento):存储沙发状态,构造函数由发起人友元访问;

  2. 定义发起人类(Sofa):即实木沙发,包含状态属性和“保存状态”“恢复状态”的方法;

  3. 定义管理者类(Caretaker):用容器存储多个备忘录,提供“添加快照”“获取快照”的接口;

  4. 主函数演示:模拟沙发生产流程,保存关键节点状态,模拟回溯场景。

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++开发中,当需要对对象状态进行版本控制或回溯时,备忘录模式无疑是最优选择之一。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐