备忘录模式
-
备忘录模式和原型模式:
备忘录模式是保存对象当时的信息 , 而原型模式是调用clone()方法进行对象的克隆 , 所以我认为可以将备忘录模式进行修改 , 保存对象信息可以直接使用clone()克隆对象 , 然后保存到备忘录中 , 但是这就涉及到浅拷贝和深拷贝的问题 , 如果我们的 “发起人类” (Originator) 内部引用类型稍微复杂的话 , 建议不去使用clone()方法 , 而直接进行信息的保存 .
-
定义:
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态 , 这样以后就可以将该对象恢复到原先保存的状态。备忘录模式就像我们大家玩的游戏存档一样 , 当我们打不过BOSS时 , 就可以先保存游戏进度 , 然后再去进行下一步的行动 . 这样方便我们再次从想要的位置开始。
-
使用场景:
- 需要保存一个对象在某一个时刻的状态或部分状态。
- 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。
- 浏览器回退、数据库备份和恢复、编辑器撤销和还原、虚拟机生成快照和恢复、Git版本控制、棋牌类游戏悔棋等等。
-
UML:
- Main:它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
- Memento:存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
- Gamer:负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
-
优点:
- 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
- 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
-
缺点:
- 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
-
样例:
public class Memento { private int menoy; private ArrayList fruits; //窄接口,访问部分信息 public int getMenoy(){ return menoy; } //宽接口,本包之内皆可访问 Memento(int menoy){ this.menoy=menoy; fruits=new ArrayList();//每次调用的时候重新生成,很重要 } //宽接口,本包之内皆可访问 List getFruits(){ return (List) fruits.clone(); } //宽接口,本包之内皆可访问 void addFruits(String fruit){ fruits.add(fruit); } }
package zyr.dp.memento; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; public class Gamer { private static String[] FruitsSame={"香蕉","苹果","橘子","柚子"}; private int menoy; private List fruits=new ArrayList(); private Random random=new Random(); public int getMenoy(){ return menoy; } public Gamer(int menoy){ this.menoy=menoy; } public void bet(){ int next=random.nextInt(6)+1; if(next==1){ menoy+=100; System.out.println("金钱增加了100,当前金钱为:"+menoy); }else if(next==2){ menoy/=2; System.out.println("金钱减少了一半,当前金钱为:"+menoy); }else if(next==6){ String f=getFruit(); fruits.add(f); System.out.println("获得了水果:"+f+",当前金钱为:"+menoy); }else { System.out.println("金钱没有发生改变,当前金钱为:"+menoy); } } private String getFruit() { String prefix=""; if(random.nextBoolean()){ prefix="好吃的"; } return prefix+FruitsSame[random.nextInt(FruitsSame.length)]; } public Memento createMemento(){ Memento m=new Memento(menoy); Iterator it=fruits.iterator(); while(it.hasNext()){ String fruit=(String)it.next(); if(fruit.startsWith("好吃的")){ m.addFruits(fruit); } } return m; } public void restoreMemento(Memento memento){ this.menoy=memento.getMenoy(); this.fruits=memento.getFruits(); } public String toString(){ return "Menoy:"+menoy+" ,Fruits:"+fruits; } }
package zyr.dp.test; import zyr.dp.memento.Gamer; import zyr.dp.memento.Memento; public class Main { 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.getMenoy()); gamer.bet(); if(gamer.getMenoy()<memento.getMenoy()/2){ System.out.println("金钱过少,恢复到以前的状态:"); gamer.restoreMemento(memento); System.out.println("此时状态为:"+gamer); }else if(gamer.getMenoy()>memento.getMenoy()){ System.out.println("金钱增多,保存当前状态:"); memento=gamer.createMemento(); System.out.println("此时状态为:"+gamer); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
来源:CSDN
作者:嘿呦喂!!
链接:https://blog.csdn.net/qq_40788718/article/details/104416039