设计模式学习笔记——备忘录(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() +
"元。");
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(
"");
}
}
}
运行结果
当前状态:Gamer{ money=100, fruits=[]}
什么都没有发生。
所持金钱为100元。
当前状态:Gamer{ money=100, fruits=[]}
所持金钱减半了。
所持金钱为50元。
当前状态:Gamer{ money=50, fruits=[]}
所持金钱减半了。
所持金钱为25元。
(所持金钱减少了许多,因此将游戏恢复至以前的状态)
当前状态:Gamer{ money=100, fruits=[]}
获得了水果(好吃的葡萄)。
所持金钱为100元。
当前状态:Gamer{ money=100, fruits=[好吃的葡萄]}
所持金钱增加了。
所持金钱为200元。
(所持金钱增加了许多,因此保存游戏当前的状态)
当前状态:Gamer{ money=200, fruits=[好吃的葡萄]}
所持金钱减半了。
所持金钱为100元。
当前状态:Gamer{ money=100, fruits=[好吃的葡萄]}
获得了水果(葡萄)。
所持金钱为100元。
当前状态:Gamer{ money=100, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
所持金钱减半了。
所持金钱为100元。
当前状态:Gamer{ money=100, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
什么都没有发生。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为300元。
(所持金钱增加了许多,因此保存游戏当前的状态)
当前状态:Gamer{ money=300, fruits=[好吃的葡萄, 葡萄]}
所持金钱增加了。
所持金钱为400元。
(所持金钱增加了许多,因此保存游戏当前的状态)
当前状态:Gamer{ money=400, fruits=[好吃的葡萄, 葡萄]}
所持金钱减半了。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄]}
获得了水果(葡萄)。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄]}
获得了水果(好吃的橘子)。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
什么都没有发生。
所持金钱为200元。
当前状态:Gamer{ money=200, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
所持金钱增加了。
所持金钱为300元。
当前状态:Gamer{ money=300, fruits=[好吃的葡萄, 葡萄, 葡萄, 好吃的橘子]}
所持金钱减半了。
所持金钱为150元。
(所持金钱减少了许多,因此将游戏恢复至以前的状态)
当前状态: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
——————参考《图解设计模式》