设计模式 ( 二十) 备忘录模式

目录

一、定义

二、备忘录案例分析

三、总结


一、定义

备忘录模式提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,其定义如下:

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

备忘录模式属于行为型模式,通俗来讲,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法。其实有很多备忘录模式的例子,像我们平常使用的开发工具的Ctrl + Z就提供了一种撤销的功能、事务回滚等都是备忘录模式的范例。

备忘录模式的通用类图如下:

角色分析:

  • Original发起人角色: 记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据;
  • Memento备忘录角色:负责存储发起人角色的内部状态,在需要的时候提供发起人需要的内部状态;
  • Caretaker备忘录管理者角色:管理多个备忘录对象,通常用集合进行保存,建议使用Map进行保存,因为可以保存多个原始对象不同时间的备忘录对象;

二、备忘录案例分析

经常玩游戏的小伙伴都知道,有些游戏角色在死亡之后,还能立马复活,恢复到死亡之前的状态,接下来我们就是用备忘录模式来模拟这一场景。先来看下整体的类图:

 首先我们先创建一个发起人角色,其提供了创建备忘录和恢复备忘录的方法:

/**
 * 发起人角色:英雄
 */
public class GameHero {
    /**
     * 攻击力
     */
    private int aggressivity;
    /**
     * 防御力
     */
    private int defense;

    public GameHero(int aggressivity, int defense) {
        this.aggressivity = aggressivity;
        this.defense = defense;
    }

    public int getAggressivity() {
        return aggressivity;
    }

    public void setAggressivity(int aggressivity) {
        this.aggressivity = aggressivity;
    }

    public int getDefense() {
        return defense;
    }

    public void setDefense(int defense) {
        this.defense = defense;
    }

    /**
     * 保存当前的状态
     * 实质上就是创建一个备忘录对象,可以简单理解为创建一个副本
     *
     * @return
     */
    public Memento saveMemento() {
        return new Memento(this.aggressivity, this.defense);
    }

    /**
     * 恢复之前的状态
     * 从之前的副本中恢复过来
     *
     * @param memento
     */
    public void restoreMemento(Memento memento) {
        this.aggressivity = memento.getAggressivity();
        this.defense = memento.getDefense();
    }

    @Override
    public String toString() {
        return "GameHero{" +
                "攻击力=" + aggressivity +
                ", 防御力=" + defense +
                '}';
    }

}

接着定义我们的备忘录角色,它具有和发起人相类似的属性:

/**
 * 备忘录角色: 通常具有跟发起人角色相同或者少一些属性
 */
public class Memento {
    /**
     * 攻击力
     */
    private int aggressivity;
    /**
     * 防御力
     */
    private int defense;

    public Memento(int aggressivity, int defense) {
        this.aggressivity = aggressivity;
        this.defense = defense;
    }

    public int getAggressivity() {
        return aggressivity;
    }

    public void setAggressivity(int aggressivity) {
        this.aggressivity = aggressivity;
    }

    public int getDefense() {
        return defense;
    }

    public void setDefense(int defense) {
        this.defense = defense;
    }
}

最后需要一个备忘录的管理者:这里我们使用栈结构保存备忘录,具体代码如下:

/**
 * 管理者角色
 */
public class Caretaker {
    /**
     * 使用栈结构保存备忘录
     */
    private Stack<Memento> mementos = new Stack<>();
    private int index = -1;

    public void save(Memento memento) {
        while (index < mementos.size() - 1) {
            //将栈中在当年索引index之上的所有元素出栈
            mementos.pop();
        }
        mementos.push(memento);
        index++;
    }

    public Memento reserve() {
        index = index >= 0 ? index - 1 : 0;
        return mementos.get(index);
    }

    public Memento redoReserve() {
        index = index < (mementos.size() - 1) ? index + 1 : (mementos.size() - 1);
        return mementos.get(index);
    }

}

我们写一个场景类测试一下:

/**
 * 场景类
 */
public class Client {
    private static Caretaker caretaker = new Caretaker();

    public static void main(String[] args) {
        GameHero gameHero = new GameHero(10, 10);
        saveCurrentState(gameHero);

        gameHero = new GameHero(20, 20);
        saveCurrentState(gameHero);

        gameHero = new GameHero(30, 30);
        saveCurrentState(gameHero);

        gameHero = new GameHero(40, 40);
        saveCurrentState(gameHero);

        reserveState(gameHero);
        reserveState(gameHero);

        gameHero = new GameHero(50, 50);
        saveCurrentState(gameHero);

        redoReserveState(gameHero);
    }

    private static void saveCurrentState(GameHero gameHero) {
        System.out.println("******保存状态******");
        //生成一个副本
        Memento memento = gameHero.saveMemento();
        //保存备忘录到栈中
        caretaker.save(memento);
        System.out.println(gameHero);
    }

    private static void reserveState(GameHero gameHero) {
        System.out.println("******恢复状态******");
        gameHero.restoreMemento(caretaker.reserve());
        System.out.println(gameHero);
    }

    private static void redoReserveState(GameHero gameHero) {
        System.out.println("******撤销恢复状态******");
        gameHero.restoreMemento(caretaker.redoReserve());
        System.out.println(gameHero);
    }

}

程序运行结果:

******保存状态******
GameHero{攻击力=10, 防御力=10}
******保存状态******
GameHero{攻击力=20, 防御力=20}
******保存状态******
GameHero{攻击力=30, 防御力=30}
******保存状态******
GameHero{攻击力=40, 防御力=40}
******恢复状态******
GameHero{攻击力=30, 防御力=30}
******恢复状态******
GameHero{攻击力=20, 防御力=20}
******保存状态******
GameHero{攻击力=50, 防御力=50}
******撤销恢复状态******
GameHero{攻击力=50, 防御力=50}

可以看到,通过备忘录模式,我们实现了保存和撤销某个对象状态的功能。

三、总结

优点:

  • 提供了一种方式可以撤销回退到历史某个状态,保留了原始对象的状态信息;

缺点:

  • 如果原始对象的属性太多的话,那么备忘录对象也会有很多属性,这样就会创建比较多这样的对象,每一次保存原始对象状态都会耗费很多内存;

使用场景:

  • 需要保存和恢复数据的相关状态场景;
  • 提供一个可回滚的操作;
  • 需要监控的副本场景中;
  • 数据库连接的事务管理;

注意事项:

  • 备忘录的生命周期

备忘录创建出来就要在最近的代码中使用,要主动管理它的生命周期,建议就要使用,不适用就要立即删除其引用,等待垃圾回收器对它的回收。

  • 备忘录的性能

不要在频繁建立备份的场景中使用备忘录模式,比如一个for循环中,原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的,系统的性能需要考虑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值