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

    xiaoxiao2021-03-28  42

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

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

    设计模式学习笔记备忘录Memento模式 基本介绍备忘录案例 类图实现代码 Memento类Gamer类测试类运行结果 备忘录模式中的角色 Originator生成者Memento纪念品Caretaker负责人类图

    基本介绍

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

    备忘录案例

    类图

    实现代码

    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

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

    转载请注明原文地址: https://ju.6miu.com/read-664662.html

    最新回复(0)