2020年11月29日15:25:07
引子
曾经有一份真挚的爱情摆在我的面前,但是我没有珍惜,等我失去后才后悔莫及,尘世间最痛苦的事情莫过于此。
如果上天能够给我一个再来一次的机会,我会对那个女孩说三个字:我爱你。
如果非要在这份爱上加一个期限,我希望是一万年!
——至尊宝
定义
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. ——《Design Patterns: Elements of Reusable Object-Oriented Software》
备忘录模式(memento pattern),在不破坏封装型的前提下,获取并保存一个对象的内部状态,以便以后对象可以恢复到这个状态。——《设计模式:可复用面向对象软件的基础》
图示
备忘录模式结构图:
结构图显示了备忘录模式的对象角色有三,一是发起人(Originator), 二是备忘录(Memento),三是管理者(Caretaker)。
备忘录模式流程图:
流程图展示了备忘录模式的工作流程:
1、管理者(Caretaker)调用发起人(Originator)createMemento创建备忘录保存状态
2、管理者调用发起人的setMemento从备忘录获取备份的状态恢复
角色
发起人(Originator):从结构图中的两个方法createMemento和setMemento可以看出,发起人负责的是创建一个备忘录Memento,以及在需要的时候使用Memento恢复自己的内部状态。发起人可以根据需要决定保存和恢复哪些内部状态。
备忘录(Memento):负责存在发起人(Originator)对象的内部状态,并可防止Originator以外的其他对象访问备忘录(Memento)。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象;Originator能够看到一个宽接口,允许他访问返回到先前状态所需的所有数据。怎么做?Memento作为Originator的内部类就是可以实现了。
管理者(Caretaker):负责保存好备忘录,不能对备忘录的内容进行操作或检查。
代码示例
故事背景:
小时候,总想着长大,但是谁知道长大后一点有不好玩,朝九晚九。
我想玩电视剧回到年轻的剧情,让我好好感受在春天放牛,夏天放鸭,秋天抓鱼,冬天等小猪出生的日子。
有天我捡到一个相框,只要我把小时候的照片放到相框里,我就可以回到小时候。
发起人(Originator)和备忘录(Memento):
代码语言:javascript复制// 我
public class I {
private int age;
private String doingWhat;
public Photo createPhoto() {
return new Photo(this.age, this.doingWhat);
}
public void backToThePast(Photo photo) {
this.age = photo.getAge();
this.doingWhat = photo.getDoingSomething();
System.out.println("时光倒流,我现在是" age "岁,可以:" doingWhat);
}
public void setAge(int age) {
this.age = age;
}
public void setDoingWhat(String doingWhat) {
this.doingWhat = doingWhat;
}
public void print() {
System.out.println("我现在是" age "岁,可以:" doingWhat);
}
// 照片(备忘录)
public class Photo {
private int age;
private String doingWhat;
public Photo(int age, String doingWhat) {
this.age = age;
this.doingWhat = doingWhat;
}
private int getAge() {
return age;
}
private String getDoingSomething() {
return doingWhat;
}
}
}
作为备忘录角色的照片(photo)是我(I)的内部类,对我(I)来说是拥有宽接口包含getAge、getDoingSomething等方法,当然你也可以加其他方法,而这些方法对于其他对象是不可见的。
管理者(Caretaker):
代码语言:javascript复制// 时光倒流相框
public class Frame {
private List<I.Photo> photoList = new ArrayList<>();
public I.Photo getPhoto(int i) {
return photoList.get(i);
}
public void add(I.Photo photo) {
photoList.add(photo);
}
}
测试类:
代码语言:javascript复制public class MementoPatternTest {
public static void main(String[] args) {
I i = new I();
Frame frame = new Frame();
i.setAge(12);
i.setDoingWhat("春天放牛,夏天放鸭,秋天抓鱼,冬天等小猪出生");
i.print();
frame.add(i.createPhoto());
i.setAge(18);
i.setDoingWhat("念书");
frame.add(i.createPhoto());
i.print();
i.setAge(25);
i.setDoingWhat("工作");
i.print();
i.backToThePast(frame.getPhoto(0));
}
}
测试结果图:
使用场景
如果你需要创建对象状态的快照来恢复对象之前的状态时,你可以使用备忘录模式。
如果直接访问对象的状态会破坏封装,可以使用备忘录模式。
优点
- 你可以在不破坏对象封装情况的前提下创建对象状态快照。
缺点
- 如果客户端过于频繁地创建备忘录, 程序将消耗大量内存。
- 管理者必须完整跟踪原发器的生命周期, 这样才能销毁弃用的备忘录。一般来说是这样的,但是Java有垃圾回收器,管理者不存在,备忘录也会被自动回收。
总结
备忘录模式,在不破坏封装型的前提下,获取并保存一个对象的内部状态,以便以后对象可以恢复到这个状态。该模式有三个角色:发起人(Originator)、备忘录(Memento)、管理者(caretaker)。发起人创建备忘录和利用备忘录恢复状态,备忘录保存发起人状态,管理者保存备忘录。
2020年11月29日21:48:47