Java设计模式之java备忘录模式详解

目录
  • 引言
  • 备忘录模式(Memento Pattern)
  • 角色
  • 为什么会出现守护者对象(负责人)?
  • 备忘录模式实现框架
  • 下棋案例
  • 备忘录模式总结
    • 优点
    • 缺点
  • 适用场景
  • 注意细节
  • 参考文章
  • 总结

引言

备忘录模式经常可以遇到,譬如下面这些场景:

  • 浏览器回退:浏览器一般有浏览记录,当我们在一个网页上点击几次链接之后,可在左上角点击左箭头回退到上一次的页面,然后也可以点击右箭头重新回到当前页面
  • 数据库备份与还原:一般的数据库都支持备份与还原操作,备份即将当前已有的数据或者记录保留,还原即将已经保留的数据恢复到对应的表中
  • 编辑器撤销与重做:在编辑器上编辑文字,写错时可以按快捷键 Ctrl + z 撤销,撤销后可以按 Ctrl + y 重做
  • 虚拟机生成快照与恢复:虚拟机可以生成一个快照,当虚拟机发生错误时可以恢复到快照的样子
  • Git版本管理:Git是最常见的版本管理软件,每提交一个新版本,实际上Git就会把它们自动串成一条时间线,每个版本都有一个版本号,使用 git reset --hard 版本号 即可回到指定的版本,让代码时空穿梭回到过去某个历史时刻
  • 棋牌游戏悔棋:在棋牌游戏中,有时下快了可以悔棋,回退到上一步重新下

备忘录模式(Memento Pattern)

在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

角色

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

备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计。

说明:如果希望保存多个originator对象的不同时间的状态,也可以,只需要 HashMap <String, 集合>

为什么会出现守护者对象(负责人)?

举个例子说明,下棋软件要提供“悔棋”功能,用户走错棋或操作失误后可恢复到前一个步骤。悔棋可能回到上一步,也有可能回到上上次的状态…因此需要记录多次的状态

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

备忘录模式实现框架

originator : 对象(需要保存状态的对象)

public class Originator {
    private String state;//状态
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    //编写一个方法,可以保存一个状态对象Memento
    public Memento saveStateMemento(){
        return new Memento(state);
    }
    public void getStateFromMemento (Memento memento){
        state = memento.getState();
    }
}

Memento : 备忘录对象,负责保存好记录,即Originator内部状态

public class Memento {
    private String state;
    public Memento(String state) {
        this.state = state;
    }
    public String getState() {
        return state;
    }
}

Caretaker: 守护者对象,负责保存多个备忘录对象, 使用集合管理,提高效率

public class Caretaker {
    //List集合中会有很多备忘录对象
    private List<Memento> mementoList = new ArrayList<>();
    public void add(Memento memento) {
        mementoList.add(memento);
    }
    //获取到第index个Origintor的备忘录对象
    public Memento get(int index){
        return mementoList.get(index);
    }
}

下棋案例

棋子类 Chessman,原发器角色

//原发器,需要保存对象的状态
@Data
@AllArgsConstructor
public class Chessman
{
    private String label;//当前棋子的名字: 车,炮,马
    private Integer x,y;//当前棋子的坐标
    //保存当前对象的状态--备份数据
    public ChessmanMemento save()
    {
        return new ChessmanMemento(label,x,y);
    }
    //恢复当前对象的状态
    public void restore(ChessmanMemento chessmanMemento)
    {
        this.label=chessmanMemento.getLabel();
        this.x=chessmanMemento.getX();
        this.y=chessmanMemento.getY();
    }
    //展示当前对象的状态
    public void show()
    {
        System.out.println(
                String.format("棋子: %s ,位置: [%d,%d]",label,x,y)
        );
    }
}

备忘录角色 ChessmanMemento

//负责备份的棋子状态
@Data
@AllArgsConstructor
public class ChessmanMemento
{
    private String label;
    private Integer x,y;
}

负责人角色 MementoCaretaker

//负责保存多个备份对象
public class MementoCaretaker
{
   //记录当前所处的备份状态
   Integer index=-1;//一开始没有备份数据
  //通过一个List集合保存多个备份对象
    List<ChessmanMemento> chessmanMementoLinkedList= Lists.newLinkedList();
  //悔棋操作--恢复到上一个备忘录状态
    public  ChessmanMemento getMemento()
    {
        if(index<=0)
        {
            throw new IndexOutOfBoundsException("已经无棋可悔了");
        }
        this.index--;//当前所处的备份状态减去一
        //将当前状态之后的状态全部清空
        //保留前index个元素,并将流收集到List中
        chessmanMementoLinkedList = chessmanMementoLinkedList.stream()
                .limit(this.index+1).collect(Collectors.toList());
        return chessmanMementoLinkedList.get(index);
    }
    //下棋---增加新的备份对象
    public void addMemento(ChessmanMemento chessmanMemento)
    {
        index++;
        chessmanMementoLinkedList.add(chessmanMemento);
    }
}

棋子客户端,维护了一个 MementoCaretaker 对象

//客户端
public class Client
{
  //维护一个守护者对象
  MementoCaretaker mementoCaretaker=new MementoCaretaker();
 //下棋
    public void play(Chessman chessman)
    {
        //通过调用备份返回,返回一个备份对象,添加进备份集合中去
        mementoCaretaker.addMemento(chessman.save());
    }
 //悔棋
    public void undo(Chessman chessman)
    {
        //得到上一次记录的备份状态对象
        ChessmanMemento memento = mementoCaretaker.getMemento();
        //调用恢复功能
        chessman.restore(memento);
    }
}

测试

public class Test
{
    public static void main(String[] args) {
        //创建棋子对象
        Chessman chessman=new Chessman("车",1,1);
        //创建一个客户端
        Client client=new Client();
        client.play(chessman);
        chessman.show();
        chessman=new Chessman("马",2,0);
        client.play(chessman);
        chessman.show();
        //悔棋
        client.undo(chessman);
        chessman.show();
        client.undo(chessman);
        chessman.show();
    }
}

备忘录模式总结

优点

  • 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
  • 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

缺点

  • 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

适用场景

  • 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
  • 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。

注意细节

  • 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
  • 实现了信息的封装,使得用户不需要关心状态的保存细节
  • 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
  • 适用的应用场景:1、后悔药。 2、打游戏时的存档。 3、Windows 里的 ctri + z。4、IE 中的后退。4、数据库的事务管理
  • 为了节约内存,备忘录模式可以和原型模式配合使用

参考文章

备忘录模式

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

时间: 2021-09-14

详解Java设计模式之备忘录模式的使用

定义与结构     备忘录(Memento)模式又称标记(Token)模式.GOF给备忘录模式的定义为:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 在讲命令模式的时候,我们曾经提到利用中间的命令角色可以实现undo.redo的功能.从定义可以看出备忘录模式是专门来存放对象历史状态的,这对于很好的实现undo.redo功能有很大的帮助.所以在命令模式中undo.redo功能可以配合备忘录模式来实现. 其实单就实现保存一个对

实例讲解JAVA设计模式之备忘录模式

在讲述这个模式之前,我们先看一个案例:游戏回档 游戏的某个场景,一游戏角色有生命力.攻击力.防御力等数据,在打Boss前和后会不一样,我们允许玩家如果感觉与Boss决斗的效果不理想,可以让游戏恢复到决斗前.下面是代码: 游戏角色类,用来存储角色的生命力.攻击力.防御力的数据. public class GameRole { private int vit;//生命力 private int atk;//攻击力 private int def;//防御力 //状态显示 public void st

Java设计模式之备忘录模式_动力节点Java学院

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

详解备忘录模式及其在Java设计模式编程中的实现

1. 定义 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 2. 使用的原因 想要恢复对象某时的原有状态. 3. 适用的情况举例 有很多备忘录模式的应用,只是我们已经见过,却没细想这是备忘录模式的使用罢了,略略举几例: eg1. 备忘录在jsp+javabean的使用: 在一系统中新增帐户时,在表单中需要填写用户名.密码.联系电话.地址等信息,如果有些字段没有填写或填写错误,当用户点击"提交"按钮时,需要在新增页面

23种设计模式(18)java备忘录模式

23种设计模式第十八篇:java备忘录模式 定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态. 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态

JAVA设计模式之备忘录模式原理与用法详解

本文实例讲述了JAVA设计模式之备忘录模式.分享给大家供大家参考,具体如下: 备忘录模式:又叫做快照模式,指在不破坏封装性的前提下,获取到一个对象的内部状态,并在对象之外记录或保存这个状态.在有需要的时候可将该对象恢复到原先保存的状态.我们相当于把对象原始状备份保留,所以叫备忘录模式. *模式 角色对象组成: 1.发起者对象:负责创建一个备忘录来记录当前对象的内部状态,并可使用备忘录恢复内部状态. 2.备忘录对象:负责存储发起者对象的内部状态,并防止其他对象访问备忘录. 3.管理者对象:负责备忘

Java设计模式之备忘录模式(Memento模式)介绍

Memento定义:memento是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到原先保存的状态. Memento模式相对也比较好理解,我们看下列代码: 复制代码 代码如下: public class Originator { private int number; private File file = null; public Originator(){} // 创建一个Memento public Memento getMemento(){ return new Me

Java设计模式详解之门面模式(外观模式)

门面模式(Facade Pattern)也叫外观模式,它隐藏系统的复杂性,并向客户端提供一个可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性,为子系统中的一组接口提供了一个统一的高层访问接口,这个接口使得子系统更容易被访问或使用.这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用. 简而言之,就是把一堆复杂的流程封装成一个接口供给用户更简单的使用,这个设计模式里有三个角色: 1)门面角色( facade ):

对比Java设计模式编程中的状态模式和策略模式

为了能在Java应用程序中正确的使用状态模式和策略模式,开发人员需要清楚地知道这两种模式之间的区别.尽管状态模式和策略模式的结构非常相似,它们又同样遵循开闭原则,都代表着SOLID设计原则的'O',但它们的意图是完全不同的.Java中的策略模式是对一组相关的算法进行封装,给调用方提供了运行时的灵活性.调用方可以在运行时选择不同的算法,而不用修改使用策略的那个Context类.使用策略模式的经典例子包括实现加密算法,压缩算法,以及排序算法.另一方面,状态模式使用一个对象可以在不同的状态下表现出不同

java设计模式学习之简单工厂模式

简单工厂模式 简单工厂模式是属于创建型模式,又叫做静态工厂方法(Static Factory Method)模式,但不属于23种GOF设计模式之一.简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例.简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现. 利用简单工厂模式设计一个计算器. 1:创建一个运算的类 package EasyFactoryModel; abstract class Operation { public double num1=0;

Java设计模式之观察者模式(Observer模式)介绍

Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循一定的编程模式,才能使自己的代码便于理解,易于交流,Observer(观察者)模式是比较常用的一个模式,尤其在界面设计中应用广泛,而本教程所关注的是Java在电子商务系统中应用,因此想从电子商务实例中分析Observer的应用. 虽然网上商店形式多样,每个站点有自己的特色,但也有其一般的共性,单就"

Java设计模式之装饰模式(Decorator模式)介绍

Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的. Decorator定义:动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活. 为什么使用Decorator 我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生