备忘录模式
定义:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。(提供江湖人称的后悔药,可以回到以前某个状态。)
动机:为了允许用户取消不确定的操作或从错误中恢复过来,需要设置备份点并提供撤销机制,而要实现这些机制,要事先将状态信息保存在某处,这样才能将对象恢复到它们原先的状态
备忘录模式的结构图
备忘录模式包含的角色:
1.原发器(Originator)
原发器可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态。
2.备忘录(Memento)
存储原发器的内部状态,根据原发器来决定保存哪些内部状态。(除了原发器本身和负责人类之外,备忘录对象不能直接供其他类使用,所以这里的类不能设为public)
3.负责人(Caretaker)
负责保存备忘录,但是不能对备忘录的内容进行操作或检查。(只负责存储,不能修改!!!)
-------------------------------------------------------------我是最渣的分割线---------------------------------------------------------------------
下面是一个例题
某软件公司正在开发一款RPG网游,为了给玩家提供更多方便,在游戏过程中设置一个恢复点,用于保存当前的游戏场景,如果在后续游戏过程中玩家角色“不幸牺牲”,可以返回到先前保存的场景,从所设恢复点开始重新游戏。试使用备忘录模式设计该功能,要求绘制相应的类图并使用Java语言编程模拟实现。
(因为老师要求保存多个状态,故在负责人类那里以数组形式对备忘录进行了存储)
以下是自己的代码实现
原发器类
1 package RPG; 2 3 public class PlayeuserInfo {// 原发器 4 private String blood;// 血量 5 private String blue;// 蓝色法力 6 private String heroname;//英雄名称 7 8 public String getBlood() { 9 return blood; 10 } 11 12 public void setBlood(String blood) { 13 this.blood = blood; 14 } 15 16 public String getBlue() { 17 return blue; 18 } 19 20 public void setBlue(String blue) { 21 this.blue = blue; 22 } 23 public String getHeroName() { 24 return heroname; 25 } 26 27 public void setHeroName(String heroName) { 28 heroname = heroName; 29 } 30 31 public Memento saveMemento() {// 保存备忘录 32 return new Memento(heroname,blood, blue); 33 } 34 35 public void restoreMemento(Memento memento) {// 重置备忘录 36 this.blood = memento.getBlood(); 37 this.blue = memento.getBlue(); 38 this.heroname=memento.getHeroName(); 39 } 40 41 public void show() {// 展示现在的状态 42 System.out.println(heroname+"现在的血量为" + this.blood + ",法力值为:" + this.blue); 43 44 } 45 46 47 48 }
这里原发器,封装了英雄名称,血量,法力值,也增加了保存备忘录和从备忘录中恢复状态的方法。
备忘录类
1 package RPG; 2 3 class Memento { 4 private String blood;// 血量 5 private String blue;// 蓝色法力 6 private String heroname;// 英雄名称 7 8 public Memento(String heroname,String blood, String blue) { 9 // TODO Auto-generated constructor stub 10 this.blood = blood; 11 this.blue = blue; 12 this.heroname = heroname; 13 } 14 15 public String getBlood() { 16 return blood; 17 } 18 19 public void setBlood(String blood) { 20 this.blood = blood; 21 } 22 23 public String getBlue() { 24 return blue; 25 } 26 27 public void setBlue(String blue) { 28 this.blue = blue; 29 } 30 31 public String getHeroName() { 32 return heroname; 33 } 34 35 public void setHeroName(String heroName) { 36 this.heroname = heroName; 37 } 38 39 }
这里因为要存储和原发器类对应的字段,因此它的成员属性和getter,setter方法都要和原发器的一样,而且要和原发器类放在同一个包,因为不能让其他类对他直接访问,故这个类不能设为public。
负责人类
1 package RPG; 2 3 import java.util.ArrayList; 4 5 public class Memento_Caretaker { 6 private ArrayList mementolist=new ArrayList();//用一个集合把所有的备忘录存储起来 7 8 public Memento getMemento(int i) {//获取备忘录 9 return (Memento) mementolist.get(i); 10 } 11 12 public void setMemento(Memento memento) {//设置备忘录 13 mementolist.add(memento); 14 } 15 }
负责人类也放在和前两个同一个包里面,但外部可以直接访问,负责人只负责存储,不能修改。
这里用了ArrayList数组形式保存多个备忘录。通过getMemento()方法来获取备忘录,通过add()方法向数组设置/增加备忘录。
客户端类
1 package RPG; 2 3 public class Client { 4 5 public static void main(String[] args) { 6 // TODO Auto-generated method stub 7 PlayeuserInfo user = new PlayeuserInfo(); 8 Memento_Caretaker mc = new Memento_Caretaker(); 9 10 user.setHeroName("王昭君"); 11 user.setBlood("5000"); 12 user.setBlue("5000"); 13 System.out.println("状态一:"); 14 user.show(); 15 mc.setMemento(user.saveMemento());// 保存备忘录 16 System.out.println("-----------------------------------"); 17 18 user.setHeroName("王昭君"); 19 user.setBlood("4280"); 20 user.setBlue("1000"); 21 System.out.println("状态二:"); 22 user.show(); 23 mc.setMemento(user.saveMemento());// 保存备忘录 24 System.out.println("-----------------------------------"); 25 26 user.setHeroName("王昭君"); 27 user.setBlood("2500"); 28 user.setBlue("2000"); 29 System.out.println("状态三:"); 30 user.show(); 31 mc.setMemento(user.saveMemento());// 保存备忘录 32 System.out.println("-----------------------------------"); 33 34 System.out.println("!!!!玩家不幸牺牲!!!!"); 35 user.restoreMemento(mc.getMemento(0)); 36 System.out.println("回到状态一"); 37 user.show(); 38 System.out.println("-----------------------------------"); 39 } 40 41 }
在客户端类这里进行原发器的创建以及负责人的创建,设置备忘录的属性,并进行保存和恢复。
输出结果:
备忘录模式的优缺点
优点:
(1)提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用先前存储起来的备忘录将状态复原。
(2)实现了信息的封装,一个备忘录对象是一种原发器对象的表示,不会被其他代码改动,这种模式简化了原发器对象,备忘录只保存原发器的状态,采用堆栈来存储备忘录对象可以实现多次撤销操作,可以通过在负责人中定义集合对象来存储多个备忘录。
缺点:
资源消耗过大。
备忘录模式的适用情况包括:
保存一个对象在某一个时刻的状态或部分状态,这样以后需要时它能够恢复到先前的状态;如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。