设计模式:备忘录(Memento)模式

设计模式:备忘录(Memento)模式

一、前言

  备忘录模式用于保存和恢复对象的状态,相信大家看过我前面的拙作就会想到原型模式也能保存一个对象在某一个时刻的状态,那么两者有何不同的呢?原型模式保存的是当前对象的所有状态信息,恢复的时候会生成与保存的对象完全相同的另外一个实例;而备忘录模式保存的是我们关心的在恢复时需要的对象的部分状态信息,相当于快照。备忘录模式大家肯定都见过,比如在玩游戏的时候有一个保存当前闯关的状态的功能,会对当前用户所处的状态进行保存,当用户闯关失败或者需要从快照的地方开始的时候,就能读取当时保存的状态完整地恢复到当时的环境,这一点和VMware上面的快照功能很类似。

二、代码

  Memento类:

 1 package zyr.dp.memento;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class Memento {
 7     
 8     private int menoy;
 9     private ArrayList fruits;
10 
11     //窄接口,访问部分信息
12     public int getMenoy(){
13         return menoy;
14     }
15     
16     //宽接口,本包之内皆可访问
17     Memento(int menoy){
18         this.menoy=menoy;
19         fruits=new ArrayList();//每次调用的时候重新生成,很重要
20     }
21     //宽接口,本包之内皆可访问
22     List getFruits(){
23         return (List) fruits.clone();
24     }
25     //宽接口,本包之内皆可访问
26     void  addFruits(String fruit){
27         fruits.add(fruit);
28     }
29 
30 }
Gamer 类:
 1 package zyr.dp.memento;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Iterator;
 5 import java.util.List;
 6 import java.util.Random;
 7 
 8 public class Gamer {
 9 
10     private static  String[] FruitsSame={"香蕉","苹果","橘子","柚子"};
11     
12     private int menoy;
13     private List fruits=new ArrayList();
14     private  Random random=new Random();
15     
16     public int getMenoy(){
17         return menoy;
18     }
19     
20     public Gamer(int menoy){
21         this.menoy=menoy;
22     }
23     
24     public void bet(){
25         int next=random.nextInt(6)+1;
26         if(next==1){
27             menoy+=100;
28             System.out.println("金钱增加了100,当前金钱为:"+menoy);
29         }else if(next==2){
30             menoy/=2;
31             System.out.println("金钱减少了一半,当前金钱为:"+menoy);
32         }else if(next==6){
33             String f=getFruit();
34             fruits.add(f);
35             System.out.println("获得了水果:"+f+",当前金钱为:"+menoy);
36         }else {
37             System.out.println("金钱没有发生改变,当前金钱为:"+menoy);
38         }
39     }
40     
41     private String getFruit() {
42 
43         String prefix="";
44         if(random.nextBoolean()){
45             prefix="好吃的";
46         }
47         return prefix+FruitsSame[random.nextInt(FruitsSame.length)];
48         
49     }
50     
51     public Memento createMemento(){
52         Memento m=new Memento(menoy);
53         Iterator it=fruits.iterator();
54         while(it.hasNext()){
55             String fruit=(String)it.next();
56             if(fruit.startsWith("好吃的")){
57                 m.addFruits(fruit);
58             }
59         }
60         return m;
61     }
62     
63     public  void restoreMemento(Memento memento){
64         this.menoy=memento.getMenoy();
65         this.fruits=memento.getFruits();
66     }
67     
68     public String toString(){
69         return "Menoy:"+menoy+" ,Fruits:"+fruits;
70     }
71     
72 }

 Main类:

 1 package zyr.dp.test;
 2 
 3 import zyr.dp.memento.Gamer;
 4 import zyr.dp.memento.Memento;
 5 
 6 public class Main {
 7 
 8     public static void main(String[] args) {
 9         Gamer gamer=new Gamer(100);
10         Memento memento=gamer.createMemento();
11         for(int i=0;i<100;i++){
12             System.out.println("当前状态:"+i);
13             System.out.println("当前金额:"+gamer.getMenoy());
14             gamer.bet();
15             if(gamer.getMenoy()<memento.getMenoy()/2){
16                 System.out.println("金钱过少,恢复到以前的状态:");
17                 gamer.restoreMemento(memento);
18                 System.out.println("此时状态为:"+gamer);
19             }else if(gamer.getMenoy()>memento.getMenoy()){
20                 System.out.println("金钱增多,保存当前状态:");
21                 memento=gamer.createMemento();
22                 System.out.println("此时状态为:"+gamer);
23             }
24             try {
25                 Thread.sleep(500);
26             } catch (InterruptedException e) {
27                 e.printStackTrace();
28             }
29         }
30 
31     }
32 
33 }

 运行结果:

当前状态:0
当前金额:100
金钱没有发生改变,当前金钱为:100
当前状态:1
当前金额:100
金钱增加了100,当前金钱为:200
金钱增多,保存当前状态:
此时状态为:Menoy:200 ,Fruits:[]
当前状态:2
当前金额:200
金钱增加了100,当前金钱为:300
金钱增多,保存当前状态:
此时状态为:Menoy:300 ,Fruits:[]
当前状态:3
当前金额:300
金钱减少了一半,当前金钱为:150
当前状态:4
当前金额:150
金钱减少了一半,当前金钱为:75
金钱过少,恢复到以前的状态:
此时状态为:Menoy:300 ,Fruits:[]
当前状态:5
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:6
当前金额:300
金钱增加了100,当前金钱为:400
金钱增多,保存当前状态:
此时状态为:Menoy:400 ,Fruits:[]
当前状态:7
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:8
当前金额:400
金钱减少了一半,当前金钱为:200
当前状态:9
当前金额:200
金钱增加了100,当前金钱为:300
当前状态:10
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:11
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:12
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:13
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:14
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:15
当前金额:300
金钱增加了100,当前金钱为:400
当前状态:16
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:17
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:18
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:19
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:20
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:21
当前金额:400
金钱减少了一半,当前金钱为:200
当前状态:22
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:23
当前金额:200
获得了水果:橘子,当前金钱为:200
当前状态:24
当前金额:200
金钱增加了100,当前金钱为:300
当前状态:25
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:26
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:27
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:28
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:29
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:30
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:31
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:32
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:33
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:34
当前金额:300
获得了水果:橘子,当前金钱为:300
当前状态:35
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:36
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:37
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:38
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:39
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:40
当前金额:300
金钱减少了一半,当前金钱为:150
金钱过少,恢复到以前的状态:
此时状态为:Menoy:400 ,Fruits:[]
当前状态:41
当前金额:400
金钱减少了一半,当前金钱为:200
当前状态:42
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:43
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:44
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:45
当前金额:200
金钱减少了一半,当前金钱为:100
金钱过少,恢复到以前的状态:
此时状态为:Menoy:400 ,Fruits:[]
当前状态:46
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:47
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:48
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:49
当前金额:400
获得了水果:香蕉,当前金钱为:400
当前状态:50
当前金额:400
金钱没有发生改变,当前金钱为:400
当前状态:51
当前金额:400
金钱减少了一半,当前金钱为:200
当前状态:52
当前金额:200
金钱增加了100,当前金钱为:300
当前状态:53
当前金额:300
金钱减少了一半,当前金钱为:150
金钱过少,恢复到以前的状态:
此时状态为:Menoy:400 ,Fruits:[]
当前状态:54
当前金额:400
金钱减少了一半,当前金钱为:200
当前状态:55
当前金额:200
获得了水果:好吃的柚子,当前金钱为:200
当前状态:56
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:57
当前金额:200
获得了水果:好吃的香蕉,当前金钱为:200
当前状态:58
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:59
当前金额:200
获得了水果:香蕉,当前金钱为:200
当前状态:60
当前金额:200
获得了水果:苹果,当前金钱为:200
当前状态:61
当前金额:200
金钱没有发生改变,当前金钱为:200
当前状态:62
当前金额:200
金钱增加了100,当前金钱为:300
当前状态:63
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:64
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:65
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:66
当前金额:300
金钱没有发生改变,当前金钱为:300
当前状态:67
当前金额:300
金钱减少了一半,当前金钱为:150
金钱过少,恢复到以前的状态:
此时状态为:Menoy:400 ,Fruits:[]
当前状态:68
当前金额:400
金钱增加了100,当前金钱为:500
金钱增多,保存当前状态:
此时状态为:Menoy:500 ,Fruits:[]
当前状态:69
当前金额:500
金钱增加了100,当前金钱为:600
金钱增多,保存当前状态:
此时状态为:Menoy:600 ,Fruits:[]
当前状态:70
当前金额:600
金钱没有发生改变,当前金钱为:600
当前状态:71
当前金额:600
金钱没有发生改变,当前金钱为:600
当前状态:72
当前金额:600
金钱没有发生改变,当前金钱为:600
当前状态:73
当前金额:600
金钱增加了100,当前金钱为:700
金钱增多,保存当前状态:
此时状态为:Menoy:700 ,Fruits:[]
当前状态:74
当前金额:700
金钱增加了100,当前金钱为:800
金钱增多,保存当前状态:
此时状态为:Menoy:800 ,Fruits:[]
当前状态:75
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:76
当前金额:800
获得了水果:好吃的柚子,当前金钱为:800
当前状态:77
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:78
当前金额:800
金钱减少了一半,当前金钱为:400
当前状态:79
当前金额:400
金钱减少了一半,当前金钱为:200
金钱过少,恢复到以前的状态:
此时状态为:Menoy:800 ,Fruits:[]
当前状态:80
当前金额:800
获得了水果:好吃的苹果,当前金钱为:800
当前状态:81
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:82
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:83
当前金额:800
获得了水果:好吃的柚子,当前金钱为:800
当前状态:84
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:85
当前金额:800
金钱没有发生改变,当前金钱为:800
当前状态:86
当前金额:800
金钱增加了100,当前金钱为:900
金钱增多,保存当前状态:
此时状态为:Menoy:900 ,Fruits:[好吃的苹果, 好吃的柚子]
当前状态:87
当前金额:900
金钱减少了一半,当前金钱为:450
当前状态:88
当前金额:450
金钱增加了100,当前金钱为:550
当前状态:89
当前金额:550
金钱增加了100,当前金钱为:650
当前状态:90
当前金额:650
金钱增加了100,当前金钱为:750
当前状态:91
当前金额:750
获得了水果:柚子,当前金钱为:750
当前状态:92
当前金额:750
金钱没有发生改变,当前金钱为:750
当前状态:93
当前金额:750
金钱增加了100,当前金钱为:850
当前状态:94
当前金额:850
金钱减少了一半,当前金钱为:425
金钱过少,恢复到以前的状态:
此时状态为:Menoy:900 ,Fruits:[好吃的苹果, 好吃的柚子]
当前状态:95
当前金额:900
获得了水果:好吃的橘子,当前金钱为:900
当前状态:96
当前金额:900
金钱没有发生改变,当前金钱为:900
当前状态:97
当前金额:900
金钱增加了100,当前金钱为:1000
金钱增多,保存当前状态:
此时状态为:Menoy:1000 ,Fruits:[好吃的苹果, 好吃的柚子, 好吃的橘子]
当前状态:98
当前金额:1000
金钱减少了一半,当前金钱为:500
当前状态:99
当前金额:500
金钱减少了一半,当前金钱为:250
金钱过少,恢复到以前的状态:
此时状态为:Menoy:1000 ,Fruits:[好吃的苹果, 好吃的柚子, 好吃的橘子]
运行结果

    本程序的功能是根据循环次数随机的生成1~6这6个数字,如果数字是1,则金钱加一百,如果是二,则金钱减半,如果是6,则随机生成水果,水果分为好吃的和不好吃的,在保存的时候只保存好吃的水果,恢复的时候就只有好吃的水果了。当金钱少于当前备忘录中金钱的一半的时候就要恢复到备忘录的状态;当金钱大于备忘录的状态的时候就要备份当前的状态,备份的时候只备份好的水果以及当前金额,这就是游戏的功能,可以看到运行的结果的正确性。

  这里有几点要注意:

   2.1、窄接口和宽接口

   在代码中我已经标注出了窄接口和宽接口,如何定义这两种接口还要看这两种接口前面的修饰符,如果是默认的(只有本包的类可以使用),并且这些接口结合到一起可以完全的将本类的信息显示出来,那么就是宽接口;只能在本包之中使用,如果修饰符是public的接口,并且只能表示本类一部分信息,因为是public可以在其他包中使用的,就是窄接口,只能查看部分信息,因此是窄的。如下图所示,对于Main类所在的包,只能使用其他两个类中声明为public的字段和方法,因此在Main中只能使用窄接口来完成一定信息的读取getMenoy()。这只是一个概念,强调的是类、字段、方法的可见性。

 

    2.2、可见性

   同时我们也知道,public修饰的字段和方法在任何包中都可以使用,private修饰的字段和方法只能在本类之中使用,protected修饰的方法可以在本包之中以及该类的子类(可以在其他包)中使用,默认的没有任何修饰的可以在本包之中使用。这就是四种修饰关键字的可见性。在编程的时候我们一定要考虑这些问题,不然就会导致我们不想看到的字段、方法、类被误用的结果。

三、总结

  备忘录模式也是一种比较常用的模式用来保存对象的部分用于恢复的信息,和原型模式有着本质的区别,广泛运用在快照功能之中,另外我们知道了宽接口和窄接口,这里的接口就是指的方法,没有其他意思,以及类的可见性。同样的使用备忘录模式可以使得程序可以组件化,比如打算多次撤销当前的状态,以及不仅可以撤销而且可以将当前的状态保存到文件之中的时候,我们不需要修改Gamer的代码就能做到,职责明确是一种非常重要的是软件工程思想。

猜你喜欢

转载自www.cnblogs.com/zyrblog/p/9249660.html
今日推荐