设计模式学习笔记——备忘录(Memento)模式

设计模式学习笔记——备忘录(Memento)模式

@(设计模式)[设计模式, 备忘录模式, memento]

基本介绍

备忘录模式提供的基本功能是:保存对象状态信息(快照)、撤销、重做和历史记录。
备忘录模式一般会提供两种接口:宽接口和窄接口。通过宽接口可以获取整个对象状态,会暴露备忘录对象的内部信息。通过窄接口,只能访问有限的,开发者限制了的信息,可以有效的防止信息泄露。

备忘录案例

类图

备忘录案例类图

实现代码

Memento类
package com.pc.memento.example;

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

/**
 * 备忘录类
 * Created by Switch on 2017/3/31.
 */
public class Memento {
    /**
     * 所持金钱
     */
    private int money;
    /**
     * 获得的水果
     */
    private List<String> fruits;

    /**
     * 获取当前所持金钱
     *
     * @return 所持金钱
     */
    public int getMoney() {
        return money;
    }

    /**
     * 构造方法,初始化所持钱数
     *
     * @param money 初始化钱数
     */
    Memento(int money) {
        this.money = money;
        this.fruits = new ArrayList<>();
    }

    /**
     * 添加水果
     *
     * @param fruit 水果
     */
    void addFruit(String fruit) {
        fruits.add(fruit);
    }

    /**
     * 添加当前所持的所有水果
     *
     * @return 水果列表
     */
    List<String> getFruits() {
        return fruits;
    }
}
Gamer类
package com.pc.memento.example;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
 * 游戏者类
 * Created by Switch on 2017/3/31.
 */
public class Gamer {
    /**
     * 所持金钱
     */
    private int money;
    /**
     * 获得的水果
     */
    private List<String> fruits = new ArrayList<>();
    /**
     * 随机数生成器
     */
    private Random random = new Random();
    /**
     * 表示水果种类的数组
     */
    private static String[] fruitsname = {"苹果", "葡萄", "香蕉", "橘子"};

    /**
     * 构造方法,初始化金钱数
     *
     * @param money 金钱数
     */
    public Gamer(int money) {
        this.money = money;
    }

    /**
     * 获取当前所持金钱数
     *
     * @return 金钱数
     */
    public int getMoney() {
        return money;
    }

    /**
     * 投掷骰子进行游戏
     */
    public void bet() {
        // 获取骰子数值
        int dice = random.nextInt(6) + 1;
        if (dice == 1) {
            this.money += 100;
            System.out.println("所持金钱增加了。");
        } else if (dice == 2) {
            this.money /= 2;
            System.out.println("所持金钱减半了。");
        } else if (dice == 6) {
            String f = this.getFruit();
            System.out.println("获得了水果(" + f + ")。");
            this.fruits.add(f);
        } else {
            System.out.println("什么都没有发生。");
        }
    }

    /**
     * 创建备忘录
     *
     * @return 备忘录对象
     */
    public Memento createMemento() {
        Memento m = new Memento(money);
        Iterator<String> it = fruits.iterator();
        while (it.hasNext()) {
            String fruit = it.next();
            if (fruit.startsWith("好吃的")) {
                m.addFruit(fruit);
            }
        }
        return m;
    }

    /**
     * 撤销到指定备忘
     *
     * @param memento 备忘录对象
     */
    public void restoreMemento(Memento memento) {
        this.money = memento.getMoney();
        this.fruits = memento.getFruits();
    }

    /**
     * 获得一个水果
     *
     * @return 水果
     */
    private String getFruit() {
        String prefix = "";
        if (random.nextBoolean()) {
            prefix = "好吃的";
        }
        return prefix + fruitsname[random.nextInt(fruitsname.length)];
    }

    @Override
    public String toString() {
        return "Gamer{ money=" + money + ", fruits=" + fruits + '}';
    }
}
测试类
package com.pc.memento.example.test;

import com.pc.memento.example.Gamer;
import com.pc.memento.example.Memento;
import org.junit.Test;

/**
 * Memento Tester.
 *
 * @author Switch
 * @version 1.0
 */
public class MementoTest {
    /**
     * 测试备忘录模式
     */
    @Test
    public void testMemento() {
        Gamer gamer = new Gamer(100);
        Memento memento = gamer.createMemento();
        for (int i = 0; i < 20; i++) {
            System.out.println("==== " + i);
            System.out.println("当前状态:" + gamer);
            // 投掷骰子
            gamer.bet();

            System.out.println("所持金钱为" + gamer.getMoney() + "元。");
            // 如何处理memento
            if (gamer.getMoney() > memento.getMoney()) {
                System.out.println("    (所持金钱增加了许多,因此保存游戏当前的状态)");
                memento = gamer.createMemento();
            } else if (gamer.getMoney() < memento.getMoney() / 2) {
                System.out.println("    (所持金钱减少了许多,因此将游戏恢复至以前的状态)");
                gamer.restoreMemento(memento);
            }

            // 等待一段时间
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            System.out.println("");
        }
    }
}
运行结果
==== 0
当前状态:Gamer{ money=100, fruits=[]}
什么都没有发生。
所持金钱为100元。

==== 1
当前状态:Gamer{ money=100, fruits=[]}
所持金钱减半了。
所持金钱为50元。

==== 2
当前状态:Gamer{ money=50, fruits=[]}
所持金钱减半了。
所持金钱为25元。
    (所持金钱减少了许多,因此将游戏恢复至以前的状态)

==== 3
当前状态:Gamer{ money=100, fruits=[]}
获得了水果(好吃的葡萄)。
所持金钱为100元。

==== 4
当前状态:Gamer{ money=100, fruits=[好吃的葡萄]}
所持金钱增加了。
所持金钱为200元。
    (所持金钱增加了许多,因此保存游戏当前的状态)

==== 5
当前状态:Gamer{ money=200, fruits=[好吃的葡萄]}
所持金钱减半了。
所持金钱为100元。

==== 6
当前状态:Gamer{ money=100, fruits=[好吃的葡萄]}
获得了水果(葡萄)。
所持金钱为100元。

==== 7
当前状态:Gamer{ money=100, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为200元。

==== 8
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
所持金钱减半了。
所持金钱为100元。

==== 9
当前状态:Gamer{ money=100, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为200元。

==== 10
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
什么都没有发生。
所持金钱为200元。

==== 11
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为300元。
    (所持金钱增加了许多,因此保存游戏当前的状态)

==== 12
当前状态:Gamer{ money=300, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为400元。
    (所持金钱增加了许多,因此保存游戏当前的状态)

==== 13
当前状态:Gamer{ money=400, fruits=[好吃的葡萄, 葡萄]}
所持金钱减半了。
所持金钱为200元。

==== 14
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
获得了水果(葡萄)。
所持金钱为200元。

==== 15
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄]}
获得了水果(好吃的橘子)。
所持金钱为200元。

==== 16
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
什么都没有发生。
所持金钱为200元。

==== 17
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
所持金钱增加了。
所持金钱为300元。

==== 18
当前状态:Gamer{ money=300, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
所持金钱减半了。
所持金钱为150元。
    (所持金钱减少了许多,因此将游戏恢复至以前的状态)

==== 19
当前状态:Gamer{ money=400, fruits=[好吃的葡萄]}
什么都没有发生。
所持金钱为400元。

备忘录模式中的角色

Originator(生成者)

Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自己恢复至生成该Memento角色时的状态。在案例中,由Gamer类扮演此角色。

Memento(纪念品)

Memento角色会将Originator角色的内部信息整合在一起。在Memento角色中虽然保存了Originator角色的信息,但它不会向外部公开这些信息。
Memento角色有以下两种接口(API)。

  • wide interface——宽接口(API
    Memento角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。
  • narrow interface——窄接口(API
    Memento角色为外部的Caretaker角色提供了“窄接口(API)” 。可以通过窄接口(API)获取的Memento角色的内部信息、非常有限,因此可以有效地防止信息泄露。

通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏。
在案例中,由Memento类扮演此角色。
Originator角色和Memento角色之间有着非常紧密的联系。

Caretaker(负责人)

Caretaker角色想要保存当前的Originator角色的状态时,会通知Originator角色。Originator角色在接收到通知后会生成Memento角色的实例并将其返回给Caretaker角色。由于以后可能会用Memento实例来将Originator 恢复至原来的状态,因此Caretaker角色会一直保存Memento实例。在案例中,由测试类扮演此角色。
不过,Caretaker角色只能使用Memento角色两种接口(API)中的窄接口(API),也就是说它无法访问Memento 角色内部的所有信息。它只是将Originator角色生成的Memento角色当作一个黑盒子保存起来。
虽然Originator角色和Memento角色之间是强关联关系,但Caretaker角色和Memento角色之间是弱关联关系。Memento角色对Caretaker角色隐藏了自身的内部信息。

类图

备忘录模式类图

GitHub:DesignPatternStudy

——————参考《图解设计模式》

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值