当前位置: 首页 > news >正文

22 C++设计模式之备忘录(Memento)模式

文章目录

      • 备忘录(Memento)模式定义
      • 备忘录(Memento)模式优缺点
        • 优点
        • 缺点
      • 备忘录(Memento)模式构成与实现
        • 构成
        • 实例

备忘录(Memento)模式定义

备忘录(Memento): 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态

备忘录(Memento)模式优缺点

优点

  • 你可以在不破坏对象封装情况的前提下创建对象状态快照。
  • 你可以通过让负责人维护原发器状态历史记录来简化原发器代码

缺点

  • 如果客户端过于频繁地创建备忘录,程序将消耗大量内存。
  • 负责人必须完整跟踪原发器的生命周期,这样才能销毁弃用的备忘录。
  • 绝大部分动态编程语言(例如 PHP、 Python 和 JavaScript)不能确保备忘录中的状态不被修改。

备忘录(Memento)模式构成与实现

构成

  • 原发器(Originator)类可以生成自身状态的快照,也可以在需要时通过快照恢复自身状态。
  • 备忘录 (Memento) 是原发器状态快照的值对象 (valueobject)。通常做法是将备忘录设为不可变的,并通过构造函数一次性传递数据。
  • 负责人(Caretaker)仅知道“何时”和“为何”捕捉原发器的状态,以及何时恢复状态。负责人通过保存备忘录栈来记录原发器的历史状态。当原发器需要回溯历史状态时,负责人将从栈中获取最顶部的备忘录,并将其传递给原发器的恢复(restoration)方法。
  • 在该实现方法中,备忘录类将被嵌套在原发器中。这样原发器就可访问备忘录的成员变量和方法,即使这些方法被声明为私有。另一方面,负责人对于备忘录的成员变量和方法的访问权限非常有限:它们只能在栈中保存备忘录,而不能修改其状态。

实例

Memento.h:

#ifndef MEMENTO_H_
#define MEMENTO_H_

#include <string>

// 备忘录类保存编辑器的过往状态
class Snapshot {
 public:
    Snapshot(std::string text, int x, int y, double width)
        : text_(text), cur_x_(x), cur_y_(y), selection_width_(width) {}
    std::string get_text() {
        return text_;
    }
    int get_cur_x() {
        return cur_x_;
    }
    int get_cur_y() {
        return cur_y_;
    }
    double get_selection_width() {
        return selection_width_;
    }

 private:
    const std::string text_;
    const int cur_x_;
    const int cur_y_;
    const double selection_width_;
};

#endif  // MEMENTO_H_

Originator.h:

#ifndef ORIGINATOR_H_
#define ORIGINATOR_H_

#include <cstdio>
#include <string>
#include <memory>
#include "Memento.h"

// 原发器中包含了一些可能会随时间变化的重要数据
// 它还定义了在备忘录中保存自身状态的方法, 以及从备忘录中恢复状态的方法
class Editor {
 public:
    void setText(std::string text) {
        text_ = text;
    }
    void setCursor(int x, int y) {
        cur_x_ = x;
        cur_y_ = y;
    }
    void setSelectionWidth(double width) {
        selection_width_ = width;
    }

    // 在备忘录中保存当前的状态
    std::shared_ptr<Snapshot> createSnapshot() {
        // 备忘录是不可变的对象, 因此原发器会将自身状态作为参数传递给备忘录的构造函数
        auto res = std::make_shared<Snapshot>(text_, cur_x_, cur_y_, selection_width_);
        printf("创建编辑器快照成功, text:%s x:%d y:%d width:%.2f\n", text_.c_str(), cur_x_, cur_y_, selection_width_);
        return res;
    }

    void resotre(std::shared_ptr<Snapshot> sptr_snapshot) {
        text_ = sptr_snapshot->get_text();
        cur_x_ = sptr_snapshot->get_cur_x();
        cur_y_ = sptr_snapshot->get_cur_y();
        selection_width_ = sptr_snapshot->get_selection_width();
        printf("恢复编辑器状态成功, text:%s x:%d y:%d width:%.2f\n", text_.c_str(), cur_x_, cur_y_, selection_width_);
    }

 private:
    // 文本
    std::string text_;
    // 光标位置
    int cur_x_;
    int cur_y_;
    // 当前滚动条位置
    double selection_width_;
};

#endif  // ORIGINATOR_H_

Caretaker.h:

#ifndef CARETAKER_H_
#define CARETAKER_H_

#include <memory>
#include "Memento.h"
#include "Originator.h"

class Command {
 public:
    explicit Command(Editor* e) : editor_(e) {}
    void makeBackup() {
        backup_ = editor_->createSnapshot();
    }
    void undo() {
        if (backup_) {
            editor_->resotre(backup_);
        }
    }

 private:
    Editor *editor_;
    std::shared_ptr<Snapshot> backup_;
};

#endif  // CARETAKER_H_

main.cpp

#include "Caretaker.h"

int main() {


    system("chcp 65001");
    // 创建原发器和负责人
    Editor editor;
    Command command(&editor);

    // 定义初始状态
    editor.setText("TOMOCAT");
    editor.setCursor(21, 34);
    editor.setSelectionWidth(3.4);

    // 保存状态
    command.makeBackup();

    // 更改编辑器状态
    editor.setText("KKKK");
    editor.setCursor(111, 222);
    editor.setSelectionWidth(111.222);

    // 撤销
    command.undo();
    system("pause");

    return 0;
}

输出:

Active code page: 65001
创建编辑器快照成功, text:TOMOCAT x:21 y:34 width:3.40
恢复编辑器状态成功, text:TOMOCAT x:21 y:34 width:3.40
Press any key to continue . . .

相关文章:

  • 小物体的目标检测的研究综述
  • 一文搞定Linux信号
  • 跟着MindSpore一起学习深度概率
  • 模型的动态LOD优化
  • 人工智能学习日记------KNN分类
  • Salesforce撤离中国后,谁来缓解在华跨国企业的焦虑?
  • 分布式系列精讲 分布式系统和单体系统之间到底有什么区别?
  • 什么是物联网数据采集网关?物联网数据采集网关的特点
  • 【vue3】05. 跟着官网学习vue3
  • 金九银十,阿里高级测开给面试者的十大建议
  • 使能OpenHarmony富设备产品化落地,润和软件HH-SCDAYU110通过兼容性测评
  • Docker 之 高级篇
  • JS | “购物车”增、删、改、查的案例
  • MySql截取字符串的几个常用函数详解
  • 多线程-线程与进程、线程的实现方式(第十八天)
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 【附node操作实例】redis简明入门系列—字符串类型
  • cookie和session
  • ES6 学习笔记(一)let,const和解构赋值
  • flutter的key在widget list的作用以及必要性
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • JAVA并发编程--1.基础概念
  • Java基本数据类型之Number
  • VUE es6技巧写法(持续更新中~~~)
  • 动态魔术使用DBMS_SQL
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 新手搭建网站的主要流程
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • #Spring-boot高级
  • (09)Hive——CTE 公共表达式
  • (2015)JS ES6 必知的十个 特性
  • (C#)一个最简单的链表类
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (zt)最盛行的警世狂言(爆笑)
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)socket Aio demo
  • .Net 8.0 新的变化
  • .NET 读取 JSON格式的数据
  • .net2005怎么读string形的xml,不是xml文件。
  • .net中应用SQL缓存(实例使用)
  • .net专家(高海东的专栏)
  • /etc/sudoers (root权限管理)
  • @DataRedisTest测试redis从未如此丝滑
  • @LoadBalanced 和 @RefreshScope 同时使用,负载均衡失效分析
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [04]Web前端进阶—JS伪数组
  • [20180224]expdp query 写法问题.txt
  • [AMQP Connection 127.0.0.1:5672] An unexpected connection driver error occured
  • [BZOJ 2142]礼物(扩展Lucas定理)
  • [C#]手把手教你打造Socket的TCP通讯连接(一)
  • [C++]Leetcode17电话号码的字母组合
  • [EFI]Lenovo ThinkPad X280电脑 Hackintosh 黑苹果引导文件
  • [GN] 后端接口已经写好 初次布局前端需要的操作(例)