设计模式简讲

30. 备忘录模式

2018-07-16  本文已影响0人  Next_吴思成

定义

备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。别名为Token。

通俗理解

电脑的Ctrl+Z,相信大家用得不少,按下这个键,就能够回退到上一步了,不经感叹,如果人生也有Ctrl+Z就好了,如果有,我一定会。好了😫,人生没有后悔药,也没有Ctrl+Z,过去就让他过去吧。

程序上有呀😎!备忘录模式就是实现Ctrl+Z的过程,保存下对象的状态,然后调用回退方法的时候,就可以把对象的状态回退到上一步。当然这并不是备忘录模式的全部,除了保存对象状态外,还需要的是,不破坏封装的转下,捕获对象的状态。这个意思是,你对象本身,就不要去保存自己的状态了,而是交给外部的类来进行保存,就像照相一样,你不能把自己压成一张照片来保存当前的状态,而是要通过照相机📷,把自己保存下来。

示例

每一个人在成长过程都长高,体重也会长,这次的业务就是保存某几个年龄时候的身高与体重。

渣渣程序

人类

public class Human {
    private List<Human> saveLocal = new ArrayList<>(16);
    private int age;
    private int high;
    private int weight;
    // 构造器,getter,setter,toString方法省略
    public void save() {
        this.saveLocal.add(new Human(age, high, weight));
    }
    public void restore(int i) {
        if(i < saveLocal.size()) {
            Human human = saveLocal.get(i);
            this.age = human.getAge();
            this.high = human.getHigh();
            this.weight = human.getWeight();
            return;
        }
        System.out.println("没有保存任何副本");
    }
}

主程序入口

public class Main {
    public static void main(String[] args) {
        Human human = new Human(1,12,12);
        human.save();
        human.growUp(2,23,23);
        human.save();

        System.out.println(human);
        human.restore(0);
        System.out.println(human);
    }
}
//Human{age=2, high=23, weight=23}
//Human{age=1, high=12, weight=12}

基本上实现了保存状态的功能了,但是这种写法,一是不符合单一职责原则,在对象里面维护了保存这个对象的list列表,就像是把自己“拍”成照片一样,本身维护这个状态不应该是对象本身去弄的,涉及到职责的问题。二是,备忘录模式根本就不是这么写,要明白,所有的设计模式都是封装一层,加类。好了,第二条是我扯的,这是规则,我们就按这个规则来写我们的备忘录吧。

优化

白箱实现

人类

public class Human {
    private int age;
    private int high;
    private int weight;
    public void growUp(int age, int high, int weight) {
        this.age = age;
        this.high = high;
        this.weight = weight;
    }
    public HumanMemento save() {
        return new HumanMemento(this.age, this.high, this.weight);
    }
    public void restore(HumanMemento memento) {
        this.age = memento.getAge();
        this.high = memento.getHigh();
        this.weight = memento.getWeight();
    }
}

人类备忘录

public class HumanMemento {
    private int age;
    private int high;
    private int weight;
    // 构造器,setter,getter方法省略
}

负责人

public class MementoCaretaker {
    private List<HumanMemento> mementos = new ArrayList<>(16);
    public HumanMemento getMemento(int i) {
        return mementos.get(i);
    }
    public void setMemento(HumanMemento memento) {
        mementos.add(memento);
    }
}

程序入口

public class Main {
    private static MementoCaretaker caretaker = new MementoCaretaker();
    public static void main(String[] args) {
        Human human = new Human(1,12,12);
        caretaker.setMemento(human.save());
        human.growUp(2,23,23);
        caretaker.setMemento(human.save());
        System.out.println(human);
        human.restore(caretaker.getMemento(0));
        System.out.println(human);
        human.restore(new HumanMemento(1,1,1));
        System.out.println(human);
    }
}
//Human{age=2, high=23, weight=23}
//Human{age=1, high=12, weight=12}
//Human{age=1, high=1, weight=1}

完了?没有,上面程序中,我创建了一个新的HumanMemento并把他设置进restore方法中,那么我就能够把Human变成1岁了,合理么?显然不合理,因为这1岁根本就不是Human备份过的状态。

怎么办?也许想到了private的构造方法,但是,把所有的方法够设置成private之后,面临的问题就是Human访问不了HumanMemento,既然访问不了HumanMemento,谈什么备份呢?

幸好Java有一个内部类的概念,对于内部类,只需要知道一点就够了,外部不能访问私有内部类,通过内部类,我们就可以创造出一个安全的备忘录模式。

黑箱实现

备忘录接口

public interface IMemento {
}

原发器 人类

public class Human {
    private int age;
    private int high;
    private int weight;
    public IMemento save() {
        return new HumanMemento(this.age, this.high, this.weight);
    }
    public void restore(IMemento memento) {
        this.age = ((HumanMemento)memento).getAge();
        this.high = ((HumanMemento)memento).getHigh();
        this.weight = ((HumanMemento)memento).getWeight();
    }
    // 构造器、setter、getter省略
    private class HumanMemento implements IMemento {
        private int age;
        private int high;
        private int weight;
        // 构造器、setter、getter省略
}

负责人类

public class MementoCaretaker {
    private List<IMemento> mementos = new ArrayList<>(16);
    public IMemento getMemento(int i) {
        return mementos.get(i);
    }
    public void setMemento(IMemento memento) {
        mementos.add(memento);
    }
}

程序入口

public class Main {
    private static MementoCaretaker caretaker = new MementoCaretaker();
    public static void main(String[] args) {
        Human human = new Human(1,12,12);
        caretaker.setMemento(human.save());
        human.growUp(2,23,23);
        caretaker.setMemento(human.save());
        System.out.println(human);
        human.restore(caretaker.getMemento(0));
        System.out.println(human);
    }
}
//Human{age=2, high=23, weight=23}
//Human{age=1, high=12, weight=12}

基本完美,不完美的地方就是,还是能够创建一个对象实现IMemento,然后把这个对象作为入参。

在圈子里玩!!!

优点

  1. 保存对象状态,可以在某个时期的时候回退到某个状态;
  2. 与原发器(Human)解耦,简化原发器的代码。

缺点

  1. 保存状态需要创建新的类,消耗资源。

应用场景

  1. 需要保存历史状态的。
  2. 和命令模式接口,在命令模式中undo的。

实例

Web中Session、Cookie。

程序

e30_memento_pattern

吐槽

就是一个List(历史)列表,保存对象状态。

https://www.jianshu.com/p/ade711e0c385

上一篇 下一篇

猜你喜欢

热点阅读