zoukankan      html  css  js  c++  java
  • [设计模式] 设计模式课程(十六)-- 备忘录模式(Memento)

    概述

    • 也叫快照(SnapShot)
    • 属于行为类设计模式
    • 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态
    • 软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能回溯到对象之前处于某个点时的状态,如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现
    • 如何实现对象状态的良好保存与恢复,但同时又不会因此而破坏对象本身的封装性
    • 在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态
    • 备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态
    • Memento模式的核心是信息隐藏,即Originator需要向外接隐藏信息,保持其封装性,但同时又需要将状态保持到外界(Memento)
    • 由于现代语言(Java,C#)运行时,都具有相当的序列化支持,因此往往用效率较高,又较容易正确实现的序列化方案来实现Memento模式

    结构1

    • 原发器类:用于获取状态,生成备忘录对象(快照),并在需要时通过快照恢复状态
    • 备忘录类:用于保存状态,通常将备忘录设为不可变的,同通过构造函数一次性传递数据
    • 负责人类:仅知道“何时”及“为何”捕捉原发器的状态,以及何时恢复状态,通过备忘录栈记录原发器的历史状态,当原发器要追溯历史状态时,负责人获取栈顶备忘录,传递给原发器的恢复方法
    • 备忘录类嵌套在原发器类中,原发器就可以访问备忘录的成员变量和方法

    结构2

    • 允许存在多种不同类型的原发器和备忘录,每种原发器都和其响应的备忘录类进行交互
    • 负责人被禁止修改在备忘录中的状态,恢复方法被定义在了备忘录类中
    • 每个备忘录与创建了自身的原发器连接,原发器将自己及状态传递给备忘录的构造函数

    场景

    • 开发一款文字编辑器应用,让用户可以撤销施加在文本上的任何操作。可让程序在执行前记录所有的对象状态,并将其保存下来,当用户以后撤销某个操作时,程序将从历史记录中获取最近的快照,然后恢复所有对象的状态

    联系

    • 同时使用命令模式和备忘录模式实现撤销,命令用于对目标对象执行各种不同的操作,备忘录用于保存命令执行前对象的状态
    • 同时使用备忘模式和迭代器模式获取当前迭代器的状态,并在需要时进行回滚
    • 对象状态比较简单时,原型模式可以作为备忘录的一个简化版本

    示例1

    memento.cpp

     1 class Memento
     2 {
     3     string state;
     4     //..
     5 public:
     6     Memento(const string & s) : state(s) {}
     7     string getState() const { return state; }
     8     void setState(const string & s) { state = s; }
     9 };
    10 
    11 class Originator
    12 {
    13     string state;
    14     //....
    15 public:
    16     Originator() {}
    17     Memento createMomento() {
    18         Memento m(state);
    19         return m;
    20     }
    21     void setMomento(const Memento & m) {
    22         state = m.getState();
    23     }
    24 };
    25 
    26 int main()
    27 {
    28     Originator orginator;
    29     
    30     //捕获对象状态,存储到备忘录
    31     Memento mem = orginator.createMomento();
    32     
    33     //... 改变orginator状态
    34     
    35     //从备忘录中恢复
    36     orginator.setMomento(memento);
    37     
    38 }
    View Code

    示例2

     1 // 原发器中包含了一些可能会随时间变化的重要数据。它还定义了在备忘录中保存
     2 // 自身状态的方法,以及从备忘录中恢复状态的方法。
     3 class Editor is
     4     private field text, curX, curY, selectionWidth
     5 
     6     method setText(text) is
     7         this.text = text
     8 
     9     method setCursor(x, y) is
    10         this.curX = curX
    11         this.curY = curY
    12 
    13     method setSelectionWidth(width) is
    14         this.selectionWidth = width
    15 
    16     // 在备忘录中保存当前的状态。
    17     method createSnapshot():Snapshot is
    18         // 备忘录是不可变的对象;因此原发器会将自身状态作为参数传递给备忘
    19         // 录的构造函数。
    20         return new Snapshot(this, text, curX, curY, selectionWidth)
    21 
    22 // 备忘录类保存有编辑器的过往状态。
    23 class Snapshot is
    24     private field editor: Editor
    25     private field text, curX, curY, selectionWidth
    26 
    27     constructor Snapshot(editor, text, curX, curY, selectionWidth) is
    28         this.editor = editor
    29         this.text = text
    30         this.curX = curX
    31         this.curY = curY
    32         this.selectionWidth = selectionWidth
    33 
    34     // 在某一时刻,编辑器之前的状态可以使用备忘录对象来恢复。
    35     method restore() is
    36         editor.setText(text)
    37         editor.setCursor(curX, curY)
    38         editor.setSelectionWidth(selectionWidth)
    39 
    40 // 命令对象可作为负责人。在这种情况下,命令会在修改原发器状态之前获取一个
    41 // 备忘录。当需要撤销时,它会从备忘录中恢复原发器的状态。
    42 class Command is
    43     private field backup: Snapshot
    44 
    45     method makeBackup() is
    46         backup = editor.createSnapshot()
    47 
    48     method undo() is
    49         if (backup != null)
    50             backup.restore()
    51     // ...
    View Code

    示例3

      1 #include <string>
      2 #include <vector>
      3 #include <iostream>
      4 #include <ctime>
      5 using namespace std;
      6 
      7 class Memento{
      8     public:
      9         virtual string GetName() const = 0;
     10         virtual string date() const = 0;
     11         virtual string state() const = 0;
     12 };
     13 
     14 class ConcreteMemento:public Memento{
     15     private:
     16         string state_;
     17         string date_;
     18     public:
     19         ConcreteMemento(string state):state_(state){
     20             this->state_ = state;
     21             time_t now = time(0);
     22             this->date_ = ctime(&now);
     23         }
     24         string state() const override{
     25             return this->state_;
     26         }    
     27         string GetName() const override{
     28             return this->date_ + "/(" + this->state_.substr(0,9)+"...)";
     29         }
     30         string date() const override{
     31             return this->date_;
     32         }
     33 };
     34 
     35 class Originator{
     36     private:
     37         string state_;
     38         string GenereateRandomString(int length = 10){
     39             const char alphanum[] = 
     40                 "0123456789"
     41                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     42                 "abcdefghijklmnopqrstuvwxyz";
     43             int stringLength = sizeof(alphanum) - 1;
     44             
     45             string random_string;
     46             for(int i = 0; i < length; i ++){
     47                 random_string += alphanum[rand()%stringLength];
     48             }
     49             return random_string;
     50         }
     51     public:
     52         Originator(string state):state_(state){
     53             cout << "Originator: My initial state is: " << this->state_ << "
    ";
     54         }    
     55         
     56         void DoSomething(){
     57             cout << "Originator: I'm doing something important.
    "; 
     58             this->state_ = this->GenereateRandomString(30);
     59             cout << "Originator: and my state has changed to: " << this->state_ << "
    ";
     60         }
     61         
     62         Memento *Save(){
     63             return new ConcreteMemento(this->state_);
     64         }
     65         
     66         void Restore(Memento *memento){
     67             this->state_ = memento->state();
     68             cout << "Originator: My state has changed to: " << this->state_ << "
    ";
     69         }
     70 };
     71 
     72 class Caretaker{
     73     private:
     74         vector<Memento *> mementos_;
     75         Originator *originator_;
     76     public:
     77         Caretaker(Originator *originator):originator_(originator){
     78             this->originator_ = originator;
     79         }
     80         
     81         void Backup(){
     82             cout << "
    Caretaker: Saving Originator's state...
    ";
     83             this->mementos_.push_back(this->originator_->Save());
     84         }
     85         void Undo(){
     86             if(!this->mementos_.size()){
     87                 return;
     88             }
     89             Memento *memento = this->mementos_.back();
     90             this->mementos_.pop_back();
     91             cout << "Caretaker: Restoring state to: " << memento->GetName() << "
    ";
     92             try{
     93                 this->originator_->Restore(memento);
     94             }catch(...){
     95                 this->Undo();
     96             }
     97         }
     98         void ShowHistory() const{
     99             cout << "Caretaker: Here's the list of mementos:
    ";
    100             for(Memento *memento : this->mementos_){
    101                 cout << memento->GetName() << "
    ";
    102             }
    103         }
    104 };
    105 
    106 void ClientCode(){
    107     Originator *originator = new Originator("Super-duper-super-puper-super.");
    108     Caretaker *caretaker = new Caretaker(originator);
    109     caretaker->Backup();
    110     originator->DoSomething();
    111     caretaker->Backup();
    112     originator->DoSomething();
    113     caretaker->Backup();
    114     originator->DoSomething();
    115     cout << "
    ";
    116     caretaker->ShowHistory();
    117     cout << "
    Client: Now, let's rollback!
    
    ";
    118     caretaker->Undo();
    119     cout << "
    Client: Once more!
    
    ";
    120     caretaker->Undo();
    121     delete originator;
    122     delete caretaker;
    123 }
    124 
    125 int main(){
    126     srand(static_cast<unsigned int>(time(NULL)));
    127     ClientCode();
    128     return 0;
    129 }
    View Code

    参考

    https://refactoringguru.cn/design-patterns/memento 

  • 相关阅读:
    忽然背后冒冷汗
    随机获取中国境内ip地址的php代码
    复制粘贴的句子
    Winform使用BackGroundWorker代替线程执行后台代码
    在IE中测试调用Web Service
    在存储过程中编写正确的事务处理代码
    【转】使用HttpWebRequest POST图片等文件,带参数
    LINQ to Entities 不识别方法"System.String ToString()"
    Asp.net 出现:HTTP 错误 404.0 Not Found
    C# 用内存映射文件读取大日志文件(.log)
  • 原文地址:https://www.cnblogs.com/cxc1357/p/12320222.html
Copyright © 2011-2022 走看看