您的位置:首页 > 其它

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

2017-04-13 10:44 471 查看

设计模式学习笔记——备忘录(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

——————参考《图解设计模式》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息