浅谈Memento备忘录模式

1、前言java

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

2、代码设计模式

Memento类:dom

package zyr.dp.memento;

import java.util.ArrayList;
import java.util.List;

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);
    }

}

Gamer类:组件化

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;
    }
    
}

Main类:ui

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();
            }
        }

    }

}

运行结果:this

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

  这里有几点要注意:、.net

一、窄接口和宽接口设计

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

二、可见性

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

三、将备分内容存盘而且读取

Memento类:

package zyr.dp.serializable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Memento implements Serializable {
    
    private static final long serialVersionUID = 8497203738547925495L;
    
    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);
    }

}

Gamer类:

package zyr.dp.serializable;

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;
    }
    
}

SerializableMain类:

package zyr.dp.test;

import java.io.*;

import zyr.dp.serializable.Gamer;
import zyr.dp.serializable.Memento;


public class SerializableMain {

    private static String filename="game.dat";
    public static void main(String[] args) {
        Gamer gamer=new Gamer(100);
        Memento memento=loadMemento();
        
        if(memento==null){
             memento=gamer.createMemento();
        }else{
            System.out.println("从上次保存处开始...");
            gamer.restoreMemento(memento);
        }
        
        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();
                saveMemento(memento);
                System.out.println("此时状态为:"+gamer);
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
    private static void saveMemento(Memento memento) {
        try {
            ObjectOutput o=new ObjectOutputStream(new FileOutputStream(filename));
            o.writeObject(memento);
            o.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static Memento loadMemento() {
        Memento memento=null;
        ObjectInput in;
        try {
            in = new ObjectInputStream(new FileInputStream(filename));
            memento=(Memento)in.readObject();
            in.close();
        } catch (FileNotFoundException e) {
            System.out.println(e.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return memento;
    }

}

运行结果:

初次运行:

第二次运行:

能够看到保存和读取成功。

3、总结

备忘录模式也是一种很是常见的模式,用来保存对象的部分用于恢复的信息,和原型模式有着本质的区别,普遍运用在快照功能中,另外咱们知道了宽接口和窄接口,这里的接口指的就是方法,没其余意思,以及类的可见性。一样的使用备忘录模式可使得程序能够组件化,好比打算屡次撤销当前的状态以及不只能够撤销并且能够将当前的状态保存到文件中,咱们不须要修改Gamer的代码就能作到,职责明确是一种很是重要的软件工程思想。

 

浅谈设计模式<最通俗易懂的讲解>

相关文章
相关标签/搜索