Memento模式(备忘录设计模式)
Posted dgwblog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Memento模式(备忘录设计模式)相关的知识,希望对你有一定的参考价值。
Memento模式?
使用面向对象编程的方式实现撤销功能时,需要事先保存实例的相关状态信息。然后,在撤销时,还需要根据所保存的信息将实例恢复至原来的状态。这个时候你需要使用Memento设计模式。(以及实例实现对状态的保存)
- 关键字:
1.·Undo(撤销)
2.·Redo(重做)
3.·History(历史记录)
4。·Snapshot(快照) 破坏封装性:
将依赖于实例内部结构的代码分散地编写在程序中的各个地方,导致程序变得难以维护。- 宽窄接口
- wide interface——宽接口(APl)Memento角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。
- narrowinterface——窄接口(API)Memento角色为外部的Caretaker角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露。
通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏
- 相关设计模式
1.Command模式(第22章)在使用Command模式处理命令时,可以使用Memento模式实现撤销功能。
2.Protype模式(第6章)在Memento模式中,为了能够实现快照和撤销功能,保存了对象当前的状态。保存的信息只是在恢复状态时所需要的那部分信息。
而在Protype模式中,会生成一个与当前实例完全相同的另外一个实例。这两个实例的内容完全一样。
- State模式(第19章)在Memento模式中,是用“实例”表示状态。而在State模式中,则是用“类”表示状态。
理清职责
- 实现功能
- ·游戏是自动进行的
- ·游戏的主人公通过掷骰子来决定下一个状态
- ·当骰子点数为1的时候,主人公的金钱会增加·当骰子点数为2的时候,主人公的金钱会减少
- ·当骰子点数为6的时候,主人公会得到水果
- ·主人公没有钱时游戏就会结束
包====>>>名字=====>>>说明
game |Memento|表示Gamer状态的类
game |Gamer表示游戏主人公的类。它会生成Memento的实例进行游戏的类。它会事先保存Memento的实例,之后会根据需要恢复Gamer的状态
null | MainT 这里为了方便起见使用MainT作为责任人保存用户状态
UML
时序图:
Code
- Gamer
public class Gamer {
/**
* 下面的money 与 fruits 就是按照一般的定义方式去定义
* 但是我们提取Memento的时候需要注意这个的获取规则
*/
// 获得金钱
private int money;
// 获得的水果
private List<String> fruits=new ArrayList<>();
private Random random=new Random();
private final static String[] fruitname=new String[]{
"苹果","葡萄","香蕉","橘子"
};
public Gamer(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
/**
* 开始游戏
* 骰子结果1,2 ,6进行不同的操作
*/
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=getFruit();
System.out.println("获得了水果["+f+"]!");
this.fruits.add(f);
}else{
System.out.println("什么也不发生");
}
}
/**
* 快照方法
*/
public Memento createMemento(){
Memento memento = new Memento(this.money);
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()){
String s = iterator.next();
if(s.startsWith("好吃的")){
memento.addFruit(s);
}
}
return memento;
}
/**
* 撤销方法
*/
public void restoreMemento(Memento memento){
this.money=memento.money;
this.fruits=memento.fruits;
}
private String getFruit() {
String prefix="";
if(random.nextBoolean()){
prefix="好吃的";
}
return prefix+fruitname[random.nextInt(fruitname.length)];
}
@Override
public String toString() {
return "Gamer{" +
"money=" + money +
", fruits=" + fruits +
'}';
}
}
- Memento
public class Memento {
/**
* 使用过程中因为Memento与Gamer是强关联关系,但是又因为是在同一个game包下,
* 使用可见性修饰符显得比较重要:
* 这里的两个字段在同一个包下都是可以访问
*/
int money;
ArrayList<String> fruits;
/**
* 窄接口
*/
public int getMoney(){
return money;
}
/**
* 这里是宽接口
* @param money
*/
Memento(int money) {
this.money = money;
this.fruits = new ArrayList<>();
}
/**
* 这里是宽接口
*/
void addFruit(String fruit){
fruits.add(fruit);
}
/**
* 这里是宽接口
*/
ArrayList<String> getFruits(){
return (ArrayList<String>) fruits.clone();
}
}
- MainT
public class MainT {
/**
* 这里的状态只是单个快照点,当你需要多个快照点的时候,
* 单独创建一个snapshot类来管理,可以使用集合等,
* 这里写个例子
*/
public static void main(String[] args) {
Gamer gamer = new Gamer(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()+"元");
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){
e.printStackTrace();
}
}
}
}
public class SnapShotManger implements Serializable {
private List<Memento> mementos=new ArrayList<>();
/**
* 实现java.io.Serializable接口
* 用objectoutputstream的writeobject方法
* 用objectInputStream的 readobject方法
*/
/**
* 保存
*/
/**
* 恢复
*/
}
以上是关于Memento模式(备忘录设计模式)的主要内容,如果未能解决你的问题,请参考以下文章