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

Posted dgwblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Memento模式(备忘录设计模式)相关的知识,希望对你有一定的参考价值。

Memento模式?

使用面向对象编程的方式实现撤销功能时,需要事先保存实例的相关状态信息。然后,在撤销时,还需要根据所保存的信息将实例恢复至原来的状态。这个时候你需要使用Memento设计模式。(以及实例实现对状态的保存)

  • 关键字:
    1.·Undo(撤销)
    2.·Redo(重做)
    3.·History(历史记录)
    4。·Snapshot(快照)
  • 破坏封装性:
    将依赖于实例内部结构的代码分散地编写在程序中的各个地方,导致程序变得难以维护。

  • 宽窄接口
  1. wide interface——宽接口(APl)Memento角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。
  2. narrowinterface——窄接口(API)Memento角色为外部的Caretaker角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露。
    通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏
  • 相关设计模式
    1.Command模式(第22章)在使用Command模式处理命令时,可以使用Memento模式实现撤销功能。
    2.Protype模式(第6章)在Memento模式中,为了能够实现快照和撤销功能,保存了对象当前的状态。保存的信息只是在恢复状态时所需要的那部分信息。
    而在Protype模式中,会生成一个与当前实例完全相同的另外一个实例。这两个实例的内容完全一样。
  1. State模式(第19章)在Memento模式中,是用“实例”表示状态。而在State模式中,则是用“类”表示状态。

理清职责

  • 实现功能
  1. ·游戏是自动进行的
  2. ·游戏的主人公通过掷骰子来决定下一个状态
  3. ·当骰子点数为1的时候,主人公的金钱会增加·当骰子点数为2的时候,主人公的金钱会减少
  4. ·当骰子点数为6的时候,主人公会得到水果
  5. ·主人公没有钱时游戏就会结束

包====>>>名字=====>>>说明
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模式(备忘录设计模式)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之- 备忘录模式(Memento Pattern)

备忘录模式(Memento Pattern)

B9:备忘录模式 Memento

21备忘录模式Memento

GOF23设计模式之备忘录模式(memento)

Memento备忘录模式