行为型设计模式——备忘录模式

文章目录

备忘录模式

备忘录模式提供了一种状态恢复的实现机制 ,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,当前很多软件都提供了撤销( Undo)操作,其中就使用了备忘录模式。备忘录模式又称为标记(Token)模式。

备忘录模式是一种软件设计模式,它提供了将对象恢复到其先前状态(通过回滚撤消)的能力,在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对 象恢复到原先的状态。

结构

在备忘录模式结构图中包含如下几个角色:

  • Originator(原发器) :它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
  • Memento(备忘录) :*存储原发器的内部状态,根据原发器来决定保存哪些内部状态。*备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
  • Caretaker(负责人) :负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。

注意
在设计备忘录类时需要考虑其封装性,除了Originator类,不允许其他类来调用备忘录类Memento的构造函数与相关方法 ,如果不考虑封装性,允许其他类调用setState()等方法,将导致在备忘录中保存的历史状态发生改变,通过撤销操作所恢复的状态就不再是真实的历史状态,备忘录模式也就失去了本身的意义。

在使用Java语言实现备忘录模式时,一般通过将Memento类与Originator类定义在同一个包中来实现封装,在Java语言中可使用默认访问标识符来定义Memento类,即保证其包内可见。

实现

cpp 复制代码
// 历史备忘录类
class History
{
public:
    History(string msg) : m_msg(msg) {}
    string getHistory()
    {
        return m_msg;
    }
private:
    string m_msg;
};
cpp 复制代码
// 鬼子
class JiaoPenJi
{
public:
    //代表鬼子在我国犯下了某一个罪行,参数对应的就是相关的描述
    void setState(string msg)
    {
        m_msg = msg;
    }

    //得到鬼子犯下的罪行的相关信息
    string getState()
    {
        return m_msg;
    }

    void beiDaddyDa()
    {
        cout << "脚盆鸡被兔子狠狠地揍了又揍, 终于承认了它们之前不承认的这些罪行: " << endl;
    }

    //将鬼子的罪行封装成一个历史对象,并通过返回值传出
    History* saveHistory()
    {
        return new History(m_msg);
    }

    //从一个历史对象中,读出相关的历史信息
    void getStateFromHistory(History* history)
    {
        m_msg = history->getHistory();
    }
private:
    string m_msg;
};
cpp 复制代码
// 记录者
class Recorder
{
public:
    //添加历史信息,并保存
    void addHistory(int index, History* history)
    {
        m_history.insert(make_pair(index, history));
    }
    //从备份信息中得到想要的历史信息
    History* getHistory(int index)
    {
        if (m_history.find(index) != m_history.end())
        {
            return m_history[index];
        }
        return nullptr;
    }
private:
    map<int, History*> m_history;
};
cpp 复制代码
int main()
{
    vector<string> msg{
        "不承认偷了中国的中医!!!",
        "不承认发动了侵华战争!!!",
        "不承认南京大屠杀!!!!",
        "不承认琉球群岛和钓鱼岛是中国的领土!!!",
        "不承认731部队的细菌和人体试验!!!",
        "不承认对我国妇女做出畜生行为!!!",
        "不承认从中国掠夺的数以亿计的资源和数以万计的文物!!!",
        "我干的龌龊事儿罄竹难书, 就是不承认......"
    };
    JiaoPenJi* jiaopen = new JiaoPenJi;
    Recorder* recorder = new Recorder;
    // 把小日本的罪行记录下来
    for (int i = 0; i < msg.size(); ++i)
    {
        jiaopen->setState(msg.at(i));
        recorder->addHistory(i, jiaopen->saveHistory());
    }
    jiaopen->beiDaddyDa();
    for (int i = 0; i < msg.size(); ++i)
    {
        jiaopen->getStateFromHistory(recorder->getHistory(i));
        cout << "  -- " << jiaopen->getState() << endl;
    }
    return 0;
}

特点

⚠️备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。

主要优点

  • 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
  • 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
  • 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

适用环境

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
相关推荐
khalil10205 小时前
代码随想录算法训练营Day-55 图论06 | 108.冗余连接、109.冗余连接II
c++·算法·leetcode·图论·并查集
lljss20205 小时前
Arm GNU 工具链 命名规则
服务器·arm开发·gnu
进击的荆棘5 小时前
优选算法——字符串
开发语言·c++·算法·leetcode·字符串
山栀shanzhi5 小时前
长连接、短连接、心跳、断线重连
开发语言·网络·c++
KnowSafe5 小时前
如何用OpenSSL生成CSR文件?
服务器·https·ssl
玖釉-5 小时前
C++ 动态规划经典题:戳气球问题详解——从区间 DP 到状态转移
c++·动态规划
洛水水5 小时前
数据库连接池详解
数据库·c++·mysql
皮卡祺q5 小时前
【抽奖系统-0】Redis 缓存与 RabbitMQ 削峰实战;架构梳理
设计模式
码小猿的CPP工坊5 小时前
AI时代C++软件开发工程师的思考
c++·人工智能