一.简介
话说,我小时候玩小霸王游戏中坦克大战的闯关游戏中,哪个时候我们基本上每次过了一关都会保存进度,如果不玩了,或者是下一关太难过了每过了,这样我们就可以回到原先的时候,回到原先的进度继续游戏.这样我们就回到过去了.
二.结构
- 备忘录(Memento)角色:备忘录角色存储"备忘发起者角色"的内部状态;在黑箱和自述历史模式的情况下,不允许除了发起人之外的人访问自己.
- 备忘发起者角色:创建一个备忘录,在需要还原的时候,还原备忘录内部状态;
- 备忘录管理者角色:负责保存好备忘录.
三.代码
白箱,外部可以访问,简单而言,就是没有进行封装.
public class Memorandum {
public static void main(String[] args) {
// 发起者
Sponsor sponsor = new Sponsor();
// 管理者
Manager manager = new Manager();
// 设置内容
sponsor.setText("abc");
// 记录版本
sponsor.createManager(manager, 1);
// 修改内容
sponsor.setText("abcdef");
// 记录版本
sponsor.createManager(manager, 2);
// 修改内容
sponsor.setText("abcdefgh");
// 记录版本
sponsor.createManager(manager, 3);
// 回滚到版本1
sponsor.callBackToVersion(manager, 1);
System.out.println("第一个版本的内容是:" + sponsor.getText());
// 回滚到版本2
sponsor.callBackToVersion(manager, 2);
System.out.println("第一个版本的内容是:" + sponsor.getText());
}
}
// 操作角色 文本编辑
class TextEdit {
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
// 管理者
class Manager {
private Map<Integer, TextEdit> map = new HashMap<>();
public TextEdit getTextEdit(Integer version) {
return map.get(version);
}
// 每次取出最新的版本
public void setTextEdit(TextEdit baby, Integer version) {
map.put(version, baby);
}
}
// 发起者
class Sponsor {
private String text;
// 保存状态
public Manager createManager(Manager manager, Integer version) {
TextEdit edit = new TextEdit();
edit.setText(text);
manager.setTextEdit(edit, version);
return manager;
}
// 回滚到指定版本
public void callBackToVersion(Manager manager, Integer version) {
text = manager.getTextEdit(version).getText();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
//output
//第一个版本的内容是:abc
//第一个版本的内容是:abcdef
黑箱,外部不能操作备忘录角色,它更符合我们编程的开闭原则,对外开放访问,关闭修改.只有发起人才能对备忘角色进行修改,管理者或者是其他类不能对备忘录角色进行修改.
public class Memorandum {
public static void main(String[] args) {
// 发起者
Sponsor sponsor = new Sponsor();
// 管理者
Manager manager = new Manager();
// 设置内容
sponsor.setText("abc");
// 记录版本
sponsor.createManager(manager, 1);
// 修改内容
sponsor.setText("abcdef");
// 记录版本
sponsor.createManager(manager, 2);
// 修改内容
sponsor.setText("abcdefgh");
// 记录版本
sponsor.createManager(manager, 3);
// 回滚到版本1
sponsor.callBackToVersion(manager, 1);
System.out.println("第一个版本的内容是:" + sponsor.getText());
// 回滚到版本2
sponsor.callBackToVersion(manager, 2);
System.out.println("第一个版本的内容是:" + sponsor.getText());
}
}
// 定义接口,让备忘录角色实现,此接口为了让管理者去操作
interface A {}
// 管理者 它不能看到备忘录角色的具体的内容,因为它操作的是接口
class Manager {
private Map<Integer, A> map = new HashMap<>();
public A getTextEdit(Integer version) {
return map.get(version);
}
// 每次取出最新的版本
public void setTextEdit(A baby, Integer version) {
map.put(version, baby);
}
}
// 发起者 能知道备忘录角色的具体类容,因为备忘录角色是它的私有类
class Sponsor {
private String text;
// 保存状态
public Manager createManager(Manager manager, Integer version) {
TextEdit edit = new TextEdit(text);
manager.setTextEdit(edit, version);
return manager;
}
// 回滚到指定版本
public void callBackToVersion(Manager manager, Integer version) {
text = ((TextEdit) manager.getTextEdit(version)).getText();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
// 备忘录角色,实现了窄接口
// 注意:此类的所有属性都是私有
private class TextEdit implements A {
private String text;
private TextEdit(String text) {
this.text = text;
}
private String getText() {
return text;
}
private void setText(String text) {
this.text = text;
}
}
}
// output
//第一个版本的内容是:abc
//第一个版本的内容是:abcdef
自述历史模式,此模式把负责人和发起人放到一个类中,这是在开发中使用的最常用的
public class Memorandum {
public static void main(String[] args) {
// 发起人与负责人
Sponsor sponsor = new Sponsor();
// 设置内容
sponsor.setText("abc");
// 记录版本
sponsor.createManager(1);
// 修改内容
sponsor.setText("abcdef");
// 记录版本
sponsor.createManager(2);
// 修改内容
sponsor.setText("abcdefgh");
// 记录版本
sponsor.createManager(3);
// 回滚到版本1
sponsor.callBackToVersion(1);
System.out.println("第一个版本的内容是:" + sponsor.getText());
// 回滚到版本2
sponsor.callBackToVersion(2);
System.out.println("第一个版本的内容是:" + sponsor.getText());
}
}
// 发起者+负责人 能知道备忘录角色的具体类容,因为备忘录角色是它的私有类
class Sponsor {
private String text;
private Map<Integer, TextEdit> map = new HashMap<>();
// 每次取出最新的版本
private TextEdit getTextEdit(Integer version) {
return map.get(version);
}
// 保存版本
private void setTextEdit(TextEdit baby, Integer version) {
map.put(version, baby);
}
// 保存状态
public void createManager(Integer version) {
TextEdit edit = new TextEdit(text);
setTextEdit(edit, version);
}
// 回滚到指定版本
public void callBackToVersion(Integer version) {
text = getTextEdit(version).getText();
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
// 备忘录角色,被发起者私有
// 注意:此类的所有属性都是私有
private class TextEdit {
private String text;
private TextEdit(String text) {
this.text = text;
}
private String getText() {
return text;
}
}
}
// output
//第一个版本的内容是:abc
//第一个版本的内容是:abcdef
四.应用场景
- 必须保存某一个对象某个或者多个时刻的状态,以方便我们以后使用的时候回复到以前的状态;
- 如果用一个接口让其他对象直接得到这些状态,江湖暴露对象的实现细节并且破环对象的封装型.
五.各个角色的作用
- 备忘录角色:将发起人角色的内部状态存储下来,备忘录能根据发起人判断应该存储多少备忘录.能保护内容不被发起人之外的对象读取;
- 发起人角色:创建一个含有当前内部状态的备忘录角色,使用备忘录角色存储其内部状态;
- 负责人角色:保存备忘录角色;
六.参考文献
大话设计模式