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


    备忘录(Memento)模式定义

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

    备忘录(Memento)模式优缺点

    优点
    • 你可以在不破坏对象封装情况的前提下创建对象状态快照。
    • 你可以通过让负责人维护原发器状态历史记录来简化原发器代码
    缺点
    • 如果客户端过于频繁地创建备忘录,程序将消耗大量内存。
    • 负责人必须完整跟踪原发器的生命周期,这样才能销毁弃用的备忘录。
    • 绝大部分动态编程语言(例如 PHP、 Python 和 JavaScript)不能确保备忘录中的状态不被修改。

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

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

    Memento.h:

    #ifndef MEMENTO_H_
    #define MEMENTO_H_
    
    #include 
    
    // 备忘录类保存编辑器的过往状态
    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_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    Originator.h:

    #ifndef ORIGINATOR_H_
    #define ORIGINATOR_H_
    
    #include 
    #include 
    #include 
    #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_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    Caretaker.h:

    #ifndef CARETAKER_H_
    #define CARETAKER_H_
    
    #include 
    #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_
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出:

    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 . . .
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    List集合对象去重及按属性去重的8种方法
    使用无服务器功能的云计算成新趋势?无服务器功能的隐藏挑战
    如何构建Hive数据仓库Hive 、数据仓库的存储方式 以及hive数据的导入导出
    @AutoWired与@Resource
    12、 学习MySQL 排序
    c++--再谈模板
    Dirac delta function (狄拉克 delta 函数)
    什么是无人机全自动飞行系统?概念、构成、作用深度解析
    Effectively Learning Spatial Indices(VLDB)
    Linux 文件操作接口
  • 原文地址:https://blog.csdn.net/qq_45531502/article/details/126419573