观世音甘泉活树的故事竟然是Java设计模式:备忘录模式
定义
备忘录模式是对象的行为型模式,备忘录对象是一个用来存储另外一个对象内部状态的快照的对象,备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化存储起来,从而可以使得在将来合适的时候把这个对象还原到存储起来的状态
意图
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态
主要解决问题
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态
何时使用
很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃
优缺点
优点:
- 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这个时候使用备忘录模式,可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以保持封装的边界
- 当发起人角色的状态改变时,有可能这个状态无效,这个时候就可以使用暂时存储起来的备忘录将状态复原
缺点:
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
结构

涉及的角色:
备忘录(Memento)角色:
- 将发起人(Originator)对象的内部状态存储起来;备忘录可以根据发起人对象的判断来决定存储多少发起人(Originator)对象的内部状态
- 备忘录可以保护其内容不被发起人(Originator)对象之外的任何对象所读取,备忘录有两个等效的接口:
- 窄接口:负责人(Caretaker)对象和其他除发起人对象之外的任何对象,看到的是备忘录的窄接口,这个接口只允许它把备忘录对象传给其他的对象
- 宽接口:发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复发起人对象的内部状态
发起人(Originator)角色:
- 创建一个含有当前的内部状态的备忘录对象
- 使用备忘录对象存储其内部状态
负责人(Caretaker)角色:
- 负责保存备忘录对象
- 不检查备忘录对象的内容
白箱实现
将发起人角色的状态存储在一个大家都看的到的地方,因此是破坏封装性的

源码如下:
public class Originator {
private String state;
/** 返回一个新的备忘录对象 */
public Memento createMemento() {
return new Memento(state);
}
/** 将发起人恢复到备忘录对象所记录的状态 */
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
/** 状态的取值方法 */
public String getState() {
return this.state;
}
/** 状态的赋值方法 */
public void setState(String state) {
this.state = state;
System.out.println("当前状态:" + this.state);
}
}
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
/** 状态的取值方法 */
public String getState() {
return this.state;
}
/** 状态的赋值方法 */
public void setState(String state) {
this.state = state;
}
}
public class Caretaker {
private Memento memento;
/** 备忘录的取值方法 */
public Memento getMemento() {
return this.memento;
}
/** 备忘录的赋值方法 */
public void setMemento(Memento memento) {
this.memento = memento;
}
}
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
//改变发起人对象的状态
originator.setState("On");
//创建备忘录对象,并将发起人对象的状态存储起来
caretaker.setMemento(originator.createMemento());
//修改发起人对象的状态
originator.setState("Off");
originator.restoreMemento(caretaker.getMemento());
//恢复发起人对象的状态
System.out.println("恢复状态:" + originator.getState());
}
}
黑箱实现
将Memento类设成Originator类的内部类,在外部提供一个标识接口MementoIF给Caretaker类以及其他对象

源码如下:
public class Originator {
private String state;
public Originator() {
}
/** 返回一个新的备忘录对象 */
public MementoIF createMemento() {
return new Memento(this.state);
}
/** 将发起人恢复到备忘录对象所记录的状态 */
public void restoreMemento(MementoIF memento) {
Memento memento1 = (Memento) memento;
this.state = memento1.getState();
}
/** 状态的取值方法 */
public String getState() {
return this.state;
}
/** 状态的赋值方法 */
public void setState(String state) {
this.state = state;
System.out.println("当前状态:" + this.state);
}
/** 内部成员类,备忘录 */
protected class Memento implements MementoIF {
private String saveState;
private Memento(String state) {
this.saveState = state;
}
/** 状态的取值方法 */
private String getState() {
return this.saveState;
}
/** 状态的赋值方法 */
private void setState(String state) {
this.saveState = state;
}
}
}
public interface MementoIF {
}
public class Caretaker {
private MementoIF memento;
/** 备忘录的取值方法 */
public MementoIF getMemento() {
return this.memento;
}
/** 备忘录的赋值方法 */
public void setMemento(MementoIF memento) {
this.memento = memento;
}
}
public class Client {
private static Originator o = new Originator();
private static Caretaker c = new Caretaker();
public static void main(String[] args) {
//改变发起人对象的状态
o.setState("On");
//创建备忘录对象,并将发起人对象的状态存储起来
c.setMemento(o.createMemento());
//修改发起人对象的状态
o.setState("Off");
o.restoreMemento(c.getMemento());
//恢复发起人对象的状态
System.out.println("恢复状态:" + o.getState());
}
}
多重检查点
存储多个状态,或者叫做有多个检查点
public class Originator {
private Vector states;
private int index;
public Originator() {
this.states = new Vector();
this.index = 0;
}
/** 返回一个新的备忘录对象 */
public Memento createMemento() {
return new Memento(states, index);
}
/** 将发起人恢复到备忘录对象所记录的状态 */
public void restoreMemento(Memento memento) {
this.states = memento.getStates();
this.index = memento.getIndex();
}
/** 状态的赋值方法 */
public void setState(String state) {
this.states.addElement(state);
index++;
}
/** 打印出所有的状态 */
public void printStates() {
System.out.println("总的状态数量:" + index);
for (Enumeration e = states.elements(); e.hasMoreElements();) {
System.out.println(e.nextElement());
}
}
}
public class Memento {
private Vector states;
private int index;
public Memento(Vector states, int index) {
this.states = (Vector) states.clone();
this.index = index;
}
/** 状态的取值方法 */
public Vector getStates() {
return states;
}
/** 检查点指数的取值方法 */
public int getIndex() {
return index;
}
}
public class Caretaker {
private Originator originator;
private Vector vector = new Vector();
private int current;
public Caretaker(Originator originator) {
this.originator = originator;
this.current = 0;
}
/** 创建一个新的检查点 */
public int createMemento() {
Memento memento = originator.createMemento();
vector.addElement(memento);
return current++;
}
/** 将发起人恢复到某个检查点 */
public void restoreMemento(int index) {
Memento memento = (Memento) vector.elementAt(index);
originator.restoreMemento(memento);
}
/** 将某个检查点删除 */
public void removeMemento(int index) {
vector.removeElementAt(index);
}
}
public class Client {
private static Originator o = new Originator();
private static Caretaker c = new Caretaker(o);
public static void main(String[] args) {
//改变状态
o.setState("state 0");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 1");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 2");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 3");
//建立一个检查点
c.createMemento();
//打印所有的检查点
o.printStates();
//恢复到第1个检查点
System.out.println("恢复到第1个检查点");
c.restoreMemento(1);
o.printStates();
//恢复到第3个检查点
System.out.println("恢复到第2个检查点");
c.restoreMemento(2);
o.printStates();
}
}

观世音甘泉活树的故事
孙大圣保护唐僧西行,路过万寿山五庄观,与道童发生口角,一时发怒,把人参果树推倒;大圣只好请观世音菩萨救活人参果树
在这里,果树的状态保存在观世音的甘露之中,菩萨可以从甘露中把果树的状态恢复过来,果树是发起人角色,甘露是备忘录角色,菩萨是负责人角色
public class SweetSpringWater {
private String fruiterState;
public SweetSpringWater(String fruiterState) {
this.fruiterState = fruiterState;
}
/** 获取果树的状态 */
public String getFruiterState() {
return fruiterState;
}
/** 设置果树的状态 */
public void setFruiterState(String fruiterState) {
this.fruiterState = fruiterState;
}
}
public class Fruiter {
private String fruiterState;
/** 返回一个新的备忘录对象 */
public SweetSpringWater createMemento() {
return new SweetSpringWater(fruiterState);
}
/** 将发起人恢复到备忘录对象所记录的状态 */
public void restoreSweetSpringWater(SweetSpringWater springWater) {
this.fruiterState = springWater.getFruiterState();
}
/** 果树状态的取值方法 */
public String getState() {
return this.fruiterState;
}
/** 果树状态态的赋值方法 */
public void setState(String fruiterState) {
this.fruiterState = fruiterState;
System.out.println("当前果树状态:" + this.fruiterState);
}
}
public class Goddess {
private SweetSpringWater sweetSpringWater;
/** 备忘录的取值方法 */
public SweetSpringWater getSweetSpringWater() {
return this.sweetSpringWater;
}
/** 备忘录的赋值方法 */
public void setMemento(SweetSpringWater sweetSpringWater) {
this.sweetSpringWater = sweetSpringWater;
}
}
public class Client {
public static void main(String[] args) {
Fruiter f = new Fruiter();
Goddess g = new Goddess();
//改变发起人对象的状态
f.setState("果树被孙悟空推倒了");
//创建备忘录对象,并将发起人对象的状态存储起来
g.setMemento(f.createMemento());
//修改发起人对象的状态
f.setState("观世音用甘泉恢复果树状态");
//恢复发起人对象的状态
System.out.println("恢复状态:" + f.getState());
}
}

观世音甘泉活树的故事竟然是Java设计模式:备忘录模式的更多相关文章
- JAVA 设计模式 备忘录模式
用途 备忘录模式 (Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态. 这样以后就可将该对象恢复到原先保存的状态. 备忘录模式是一种行为型模式. 结构
- java设计模式---备忘录模式
一.引子 俗话说:世上难买后悔药.所以凡事讲究个"三思而后行",但总常见有人做"痛心疾首"状:当初我要是--.如果真的有<大话西游>中能时光倒流的& ...
- Java设计模式-备忘录模式(Memento)
主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C ...
- Java设计模式—备忘录模式
个人感觉备忘录模式是一个比较难的设计模式,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法. 定义如下:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以 ...
- Java设计模式——组合模式
JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...
- java设计模式--单列模式
java设计模式--单列模式 单列模式定义:确保一个类只有一个实例,并提供一个全局访问点. 下面是几种实现单列模式的Demo,每个Demo都有自己的优缺点: Demo1: /** * 单列模式需要满足 ...
- 3.java设计模式-建造者模式
Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...
- Java设计模式-代理模式之动态代理(附源代码分析)
Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...
- Java设计模式——外观模式
JAVA 设计模式 外观模式 用途 外观模式 (Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式是一种结构型模式. 结构
随机推荐
- 字符串拼接出现null的问题
最近在开发的过程中遇到这样的问题,原因是在做一个需求的时候,要求将解密的号码和前缀进行拼接.一开始在这个拼接的过程中,没有考虑到数据校验的问题,因为有可能他的前缀或者其他需要拼接的字段在前端传递的过程 ...
- rar密码破解工具汇总
rar密码破解工具汇总 前言 假如酷爱在网络上找各种资源的你,经历千辛万苦终于找到了一个rar打包的文件,兴奋地慌忙点击,可打开才发现是加密的,相信这样的场景很多人都遇到过,今天就针对压缩文件密码的破 ...
- Centos7 升级 sqlite3
下载地址:https://www.sqlite.org/download.html [root@djangoServer ~]# wget https://www.sqlite.org/2019/sq ...
- 后端程序员之路 30、webapi测试工具的一点想法
有了webapi,对应的,也就要有各种语言的sdk,有时候,还要有一个好用的api测试工具.sdk和api测试工具在功能上有一些异同,有时候测试工具会直接基于sdk来制作. 它们通常包含: 1.htt ...
- 从客流统计到营销赋能,Re-ID加速实体商业数字化转型 | 爱分析洞见
2020年中国实体商业受到突发疫情的重大影响.以危机为契机,实体商业加速数字化转型,利用创新应用服务自身业务.在此阶段,基于Re-ID(Person Re-identification,即行人再识别) ...
- 从JVM底层原理分析数值交换那些事
基础数据类型交换 这个话题,需要从最最基础的一道题目说起,看题目:以下代码a和b的值会交换么: public static void main(String[] args) { int a = 1, ...
- Shell脚本控制docker容器启动顺序
1.遇到的问题 在分布式项目部署的过程中,经常要求服务器重启之后,应用(包括数据库)能够自动恢复使用.虽然使用docker update --restart=always containerid能够让 ...
- 开源大屏工具 DataGear 的使用
记录一款好用的大屏工具,DataGear,官方标记为"开源免费的数据可视化分析平台". 其支持的数据集可以为SQL或HTTP API等,SQL支持MySQL等关系型数据库及Hive ...
- ClickHouse元数据异常-MySQLHandlerFactory:Failed to read RSA key pair from server
Clickhouse版本:20.3.6.40-2 clickhouse集群三个节点,一分片,三副本,三个节点数据完全一样 1. 问题描述 在使用连接工具操作时,发现其中一个节点连接拒绝,无法操作,另外 ...
- java IO NIO BIO 最权威的总结
1. BIO (Blocking I/O) 1.1 传统 BIO 1.2 伪异步 IO 1.3 代码示例 1.4 总结 2. NIO (New I/O) 2.1 NIO 简介 2.2 NIO的特性/N ...