【设计模式】行为模式——备忘录模式

行为模式——备忘录模式

一、定义

备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

二、问题

设计一个象棋悔棋游戏,可以悔棋有限步。

三、解决方案

  • 备忘录

备忘录模式将创建状态快照的工作委派给实际状态的拥有者原发器 (Originator) 对象。 这样其他对象就不再需要从 “外部” 复制编辑器状态了, 编辑器类拥有其状态的完全访问权, 因此可以自行生成快照。

备忘录模式将对象状态的副本存储在一个名为备忘录 (Memento) 的特殊对象中。 除了创建备忘录的对象外, 任何对象都不能访问备忘录的内容。 其他对象必须使用受限接口与备忘录进行交互, 它们可以获取快照的元数据 (创建时间和操作名称等), 但不能获取快照中原始对象的状态。

这种限制策略允许你将备忘录保存在通常被称为负责人 (Caretakers) 的对象中。 由于负责人仅通过受限接口与备忘录互动, 故其无法修改存储在备忘录内部的状态。 同时, 原发器拥有对备忘录所有成员的访问权限, 从而能随时恢复其以前的状态。

四、代码实现

原发器 (Originator) 类可以生成自身状态的快照, 也可以在需要时通过快照恢复自身状态。

package com.atmae.momento;

import com.sun.istack.internal.NotNull;

/**
 * @Author: Mae
 * @Date: 2022/5/12
 * @Time: 19:10
 * @Description:
 */
public class Originator {
    
    
    private Integer x;
    private Integer y;

    /**
     * 起死回生大法
     *
     * @param m 备忘录
     */
    public void restoreMemento(@NotNull Memento m) {
    
    
        this.x = m.getX();
        this.y = m.getY();
    }

    /**
     * 起死回生 保存当前坐标
     * @return 备忘录
     */
    public Memento saveMemento(){
    
    
        return new Memento(this.x,this.y);
    }
    public Integer getX() {
    
    
        return x;
    }

    public void setPosition(Integer x, Integer y) {
    
    
        this.x = x;
        this.y = y;
    }

    public Integer getY() {
    
    
        return y;
    }


    public void show() {
    
    
        System.out.println("当前位置--->x:" + this.x + ",y:" + this.y);
    }
}

备忘录 (Memento) 是原发器状态快照的值对象 (value object)。 通常做法是将备忘录设为不可变的, 并通过构造函数一次性传递数据。

package com.atmae.momento;

import com.sun.istack.internal.NotNull;

/**
 * @Author: Mae
 * @Date: 2022/5/12
 * @Time: 19:10
 * @Description:
 */
public class Memento {
    
    
    private final Integer x;
    private final Integer y;

    public Memento(Integer x,Integer y) {
    
    
        this.x = o.getX();
        this.y = o.getY();
    }

    public Integer getX() {
    
    
        return x;
    }

    public Integer getY() {
    
    
        return y;
    }

}

负责人 (Caretaker) 仅知道 “何时” 和 “为何” 捕捉原发器的状态, 以及何时恢复状态。
负责人通过保存备忘录栈来记录原发器的历史状态。 当原发器需要回溯历史状态时, 负责人将从栈中获取最顶部的备忘录, 并将其传递给原发器的恢复 方法。

package com.atmae.momento;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author: Mae
 * @Date: 2022/5/12
 * @Time: 19:10
 * @Description:
 */
public class Caretaker {
    
    
    private final List<Memento> mementoes = new ArrayList<>();

    public Memento getMemento() {
    
    
        if (mementoes.size() == 0) {
    
    
            try {
    
    
                throw new Exception("你当前没有保存备忘录");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        Memento memento = mementoes.get(mementoes.size() - 1);
        mementoes.remove(memento);
        return memento;
    }

    public void setMemento(Memento memento) {
    
    
        int maxNum = 5;
        if (mementoes.size() >= maxNum) {
    
    
            try {
    
    
                throw new Exception("你连续悔棋次数不能超过5次");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        mementoes.add(memento);
    }
}
package com.atmae.momento;

/**
 * @Author: Mae
 * @Date: 2022/5/12
 * @Time: 19:23
 * @Description:
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
        originator.setPosition(1, 1);
        originator.show();
        caretaker.setMemento(originator.saveMemento());
        originator.setPosition(2, 3);
        originator.show();

        originator.restoreMemento(caretaker.getMemento());
        originator.show();

        originator.setPosition(6, 6);
        caretaker.setMemento(originator.saveMemento());

        originator.setPosition(8, 8);
        caretaker.setMemento(originator.saveMemento());

        originator.restoreMemento(caretaker.getMemento());
        originator.show();

        originator.restoreMemento(caretaker.getMemento());
        originator.show();

    }
}

五、UML图

在这里插入图片描述

六、备忘录模式适用场景

  • 当你需要创建对象状态快照来恢复其之前的状态时, 可以使用备忘录模式。

  • 当直接访问对象的成员变量、 获取器或设置器将导致封装被突破时, 可以使用该模式。

七、总结

优点

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

缺点

  • 如果客户端过于频繁地创建备忘录, 程序将消耗大量内存。

八、与其他模式的关系

  • 有时候原型模式可以作为备忘录的一个简化版本, 其条件是你需要在历史记录中存储的对象的状态比较简单, 不需要链接其他外部资源, 或者链接可以方便地重建

猜你喜欢

转载自blog.csdn.net/weixin_51799151/article/details/124744963