Memento
概述:
保存对象状态,使用此模式可以实现:Undo(撤销),Redo(重做),History(历史记录),SnapShot(快照)
说明:
1.本文仅仅是对 图解设计模式 的简单总结(个人笔记).
2.文章中的图片均来自本书,添加水印只是为了防止盗链行为,并无侵权的想法.
综上,若侵权, 请联系我删除!
转载请标注出处!
案例:
案例说明:
搜集水果和获取金钱数的投掷骰子游戏, 游戏规则如下
- 游戏自动进行
- 游戏主人公通过掷色子决定下一个状态
- 当点数为1 时,主人公金钱会增加
- 当点数为 2时, 金钱减少
- 当点数为 6时, 主人公会得到水果
- 主人公没有金钱游戏就会结束
主要代码:
Main.java
package Gof.Memento; import Gof.Memento.game.Gamer; import Gof.Memento.game.Memento; public class Main { public static void main(String[] args) { Gamer gamer = new Gamer(100); // 最初的所持金钱数为100 Memento memento = gamer.createMemento(); // 保存最初的状态 for (int i = 0; i < 100; 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(1000); } catch (InterruptedException e) { } System.out.println(""); } } }
Game.java
package Gof.Memento.game; import java.util.*; public class Gamer { private int money; // 所持金钱 private List fruits = new ArrayList(); // 获得的水果 private Random random = new Random(); // 随机数生成器 private static String[] fruitsname = { // 表示水果种类的数组 "苹果", "葡萄", "香蕉", "橘子", }; public Gamer(int money) { // 构造函数 this.money = money; } public int getMoney() { // 获取当前所持金钱 return money; } public void bet() { // 投掷骰子进行游戏 int dice = random.nextInt(6) + 1; // 掷骰子 if (dice == 1) { // 骰子结果为1…增加所持金钱 money += 100; System.out.println("所持金钱增加了。"); } else if (dice == 2) { // 骰子结果为2…所持金钱减半 money /= 2; System.out.println("所持金钱减半了。"); } else if (dice == 6) { // 骰子结果为6…获得水果 String f = getFruit(); System.out.println("获得了水果(" + f + ")。"); fruits.add(f); } else { // 骰子结果为3、4、5则什么都不会发生 System.out.println("什么都没有发生。"); } } public Memento createMemento() { // 拍摄快照 Memento m = new Memento(money); Iterator it = fruits.iterator(); while (it.hasNext()) { String f = (String)it.next(); if (f.startsWith("好吃的")) { // 只保存好吃的水果 m.addFruit(f); } } return m; } public void restoreMemento(Memento memento) { // 撤销 this.money = memento.money; this.fruits = memento.getFruits(); } public String toString() { // 用字符串表示主人公状态 return "[money = " + money + ", fruits = " + fruits + "]"; } private String getFruit() { // 获得一个水果 String prefix = ""; if (random.nextBoolean()) { prefix = "好吃的"; } return prefix + fruitsname[random.nextInt(fruitsname.length)]; } }
时序图:
代码链接:传送门
uml综述
Originator(生成者): originator会保存自己最新状态时生成的Memento角色,当把以前保存的Memento橘色传递给originator角色事,他自己恢复至生成该Memento角色时的状态
Memento:Memento橘色回见originator橘色的内部信息整合在一起(不会向外传递这些消息)
- wide interface:指所有用于获取恢复对象状态信息的方法的集合(会暴露内部信息,所以只有originator角色使用此接口)
- narrowinterface: 可以获取originator部分信息,可以防止信息泄露
Caretaker(负责人): 只使用narrowinterface,只是将originator角色生成的Memento角色当做一个黑盒子保存起来
补充: originator 与 Memento角色是强关联,Memento与Caretaker是弱关联, Memento角色对Caretaker角色隐藏了自身的内部信息
收获:
Memento有效期: 在内存中,不必考虑有效期,在文件中,需要考虑,若我们在某个时间点将数据保存在文件中,之后又升级了应用程序,那么可能会出现与原来保存的Memento与当前的应用程序不匹配情况
这个设计模式很好的体现了public,private,protected关键字的魅力,用好关键字,会对我们的程序有着意想不到的好处
Caretaker与originator 的职责分工:
- Caretaker角色决定何时拍摄快照,何时撤销以及保存Memento角色
- originator角色的职责是生成Memento角色,和使用M接受到的Memento角色来恢复自己的状态
Caretaker与originator的职责分工可实现:
- 变更为可以多次撤销
- 变更为不仅可以撤销,还可以将现在的状态保存在文件中
相关设计模式:
- Command:使用Memento实现撤销功能
- Protype:生成完全一模一样的实例,内容完全相同,Memento保存角色当前的状态,保存的信息仅是恢复时所需的那部分信息
- State: 用类表示状态,
- Memento : 用实例表示状态
注意:
此部分内容属于对GOF Design Pattern知识的初步认知阶段,参考书籍是结城浩的《图解设计模式》,简单易懂,十分推荐!
以上内容,作者一字一句码出来的,纯属不易,欢迎大家转载,转载是还请您表明出处。另外如果我有侵权行为,请在下方留言,确认后我会及时撤销相应内容,谢谢大家!
PS:欢迎大家来到我的小站,鸣谢!