设计模式:备忘录模式(Memento)
个人比较喜欢玩单机游戏,什么仙剑、古剑、鬼泣、使命召唤、三国无双等等一系列的游戏我都玩过(现在期待凡人修仙传),对于这些游戏除了剧情好、场面大、爽快之外,还可以随时存档,等到下次想玩了又可以从刚开始的位置玩起(貌似现在的游戏都有)。这里的所谓存档就是将当前的状态记录下来,打开存档的时候只需要将这些状态读取出来即可。还有我们小时候期待长大,长大了希望回到小时候。18岁想长到28岁,到了28想还是18好!!!
上面所提到的就是“后悔药”机制。在我们生活中,做错事了我们都期望可以从新开始,希望这个世界上有后悔药可以吃,但现实是残酷的,这个世界是没有后悔药可以吃!虽然在现实社会中不可以实现,我们可以在软件的世界里实现(现在好多软件有撤销功能:Ctrl+Z不就是么)。
在应用开发中,很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有“后悔药”可吃。
备忘录模式是一种给我们的软件提供后悔药的机制,通过它可以使系统恢复到某一特定的历史状态。
一、 模式定义
所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
备忘录模式将要保存的细节给封装在备忘录中,就是那天要改变保存的细节也不会影响到客户端。
二、 模式结构
下图是备忘录模式的UML结构图:
备忘录模式主要包含入下几个角色:
Originator: 原发器。负责创建一个备忘录,用以记录当前对象的内部状态,通过也可以使用它来利用备忘录恢复内部状态。同时原发器还可以根据需要决定Memento存储Originator的那些内部状态。
Memento: 备忘录。用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。在备忘录Memento中有两个接口,其中Caretaker只能看到备忘录中的窄接口,它只能将备忘录传递给其他对象。Originator可以看到宽接口,允许它访问返回到先前状态的所有数据。
Caretaker: 负责人。负责保存好备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
在备忘录模式中,最重要的就是备忘录Memento了。我们都是备忘录中存储的就是原发器的部分或者所有的状态信息,而这些状态信息是不能够被其他对象所访问了,也就是说我们是不可能在备忘录之外的对象来存储这些状态信息,如果暴漏了内部状态信息就违反了封装的原则,故备忘录是除了原发器外其他对象都是不可以访问的。
所以为了实现备忘录模式的封装,我们需要对备忘录的访问做些控制:
对原发器:可以访问备忘录里的所有信息。
对负责人:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。
其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。
所以就备忘录模式而言理想的情况就是只允许生成该备忘录的那个原发器访问备忘录的内部状态。
典型的备忘录代码如下:
class Memento {
private String state;
public Memento(Originator o){
state = o.state;
}
public void setState(String state){
this.state=state;
}
public String getState(){
return this.state;
}
}
三、 模式实现
实现场景:我们就以游戏挑战BOSS为实现场景,在挑战BOSS之前,角色的血量、蓝量都是满值,然后存档,在大战BOSS时,由于操作失误导致血量和蓝量大量损耗,所以只好恢复到刚刚开始的存档点,继续进行大战BOSS了。这里使用备忘录模式来实现。UML结构图如下:
首先是游戏角色类:Role.java
private int bloodFlow;
private int magicPoint; public Role(int bloodFlow,int magicPoint){
this.bloodFlow = bloodFlow;
this.magicPoint = magicPoint;
} public int getBloodFlow() {
return bloodFlow;
} public void setBloodFlow(int bloodFlow) {
this.bloodFlow = bloodFlow;
} public int getMagicPoint() {
return magicPoint;
} public void setMagicPoint(int magicPoint) {
this.magicPoint = magicPoint;
} /**
* @desc 展示角色当前状态
* @return void
*/
public void display(){
System.out.println("用户当前状态:");
System.out.println("血量:" + getBloodFlow() + ";蓝量:" + getMagicPoint());
} /**
* @desc 保持存档、当前状态
* @return
* @return Memento
*/
public Memento saveMemento(){
return new Memento(getBloodFlow(), getMagicPoint());
} /**
* @desc 恢复存档
* @param memento
* @return void
*/
public void restoreMemento(Memento memento){
this.bloodFlow = memento.getBloodFlow();
this.magicPoint = memento.getMagicPoint();
}
}
备忘录:Memento.java
class Memento {
private int bloodFlow;
private int magicPoint; public int getBloodFlow() {
return bloodFlow;
} public void setBloodFlow(int bloodFlow) {
this.bloodFlow = bloodFlow;
} public int getMagicPoint() {
return magicPoint;
} public void setMagicPoint(int magicPoint) {
this.magicPoint = magicPoint;
} public Memento(int bloodFlow,int magicPoint){
this.bloodFlow = bloodFlow;
this.magicPoint = magicPoint;
}
}
负责人:Caretaker.java
public class Caretaker {
Memento memento; public Memento getMemento() {
return memento;
} public void setMemento(Memento memento) {
this.memento = memento;
} }
客户端:Client.java
public class Client {
public static void main(String[] args) {
//打BOSS之前:血、蓝全部满值
Role role = new Role(100, 100);
System.out.println("----------大战BOSS之前----------");
role.display(); //保持进度
Caretaker caretaker = new Caretaker();
caretaker.memento = role.saveMemento(); //大战BOSS,快come Over了
role.setBloodFlow(20);
role.setMagicPoint(20);
System.out.println("----------大战BOSS----------");
role.display(); //恢复存档
role.restoreMemento(caretaker.getMemento());
System.out.println("----------恢复----------");
role.display(); }
}
运行结果
四、 模式的优缺点
优点
1、 给用户提供了一种可以恢复状态的机制。可以是用户能够比较方便地回到某个历史的状态。
2、 实现了信息的封装。使得用户不需要关心状态的保存细节。
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
五、 模式适用场景
1、 需要保存一个对象在某一个时刻的状态或部分状态。
2、 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。
六、 模式总结
1、 备忘录模式可以实现在不破坏封装的前提下,捕获一个类的内部状态,并且在该对象之外保存该对象的状态,保证该对象能够恢复到历史的某个状态。
2、 备忘录模式实现了内部状态的封装,除了创建它的原发器之外其他对象都不能够访问它。
3、 备忘录模式会占用较多的内存,消耗资源。
设计模式:备忘录模式(Memento)的更多相关文章
- [工作中的设计模式]备忘录模式memento
一.模式解析 备忘录对象是一个用来存储另外一个对象内部状态的快照的对象.备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉(Capture)住,并外部化,存储起来,从而可以在将来合适的时候把 ...
- 深入浅出设计模式——备忘录模式(Memento Pattern)
模式动机 为了使软件的使用更加人性化,对于误操作,我们需要提供一种类似“后悔药”的机制,让软件系统可以回到误操作前的状态,因此需要保存用户每一次操作时系统的状态,一旦出现误操作,可以把存储的历史状态取 ...
- 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern)
原文:乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) 作者:webabc ...
- 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)
备忘录模式 Memento 沿着脚印,走过你来时的路,回到原点. 苦海翻起爱恨 在世间难逃避命运 相亲竟不可接近 或我应该相信是缘份 一首<一生所爱>触动了多少 ...
- Java 设计模式系列(十八)备忘录模式(Memento)
Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...
- 设计模式之备忘录模式(Memento)
备忘录模式(Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. Originator(发起人):负责创建一个备忘录 ...
- 二十四种设计模式:备忘录模式(Memento Pattern)
备忘录模式(Memento Pattern) 介绍在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到保存的状态. 示例有一个Message实体类,某 ...
- JAVA 设计模式 备忘录模式
用途 备忘录模式 (Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态. 这样以后就可将该对象恢复到原先保存的状态. 备忘录模式是一种行为型模式. 结构
- 备忘录模式-Memento Pattern(Java实现)
备忘录模式-Memento Pattern Memento备忘录设计模式是一个保存另外一个对象内部状态拷贝的对象,这样以后就可以将该对象恢复到以前保存的状态. 本文中的场景: 有一款游戏可以随时存档, ...
- [转] Android中的设计模式-备忘录模式
转自Android中的设计模式-备忘录模式 定义 备忘录设计模式的定义就是把对象的状态记录和管理委托给外界处理,用以维持自己的封闭性. 比较官方的定义 备忘录模式(Memento Pattern)又叫 ...
随机推荐
- NOI2019 游记
day-1 广二真好看QAQ (要是我也能在这里读书就好了) 提供的餐饮好评QAQ 发现室友是雅礼集训时候的室友,衡水小姐姐zyn. 但是寝室没有网没有信号没有桌子真的不良心啊...... 发现小卖部 ...
- Codeforces Round #564 (Div. 1)
Codeforces Round #564 (Div. 1) A Nauuo and Cards 首先如果牌库中最后的牌是\(1,2,\cdots, k\),那么就模拟一下能不能每次打出第\(k+i\ ...
- MapBox
MapBox的地图API大家用过吗 用作网站或者APP的底图,就不用自己架设地图服务器了 发布自己的地图了 这跟Google Map是一样的道理,类似的还有天地图,高德,百度地图API等等. 属于前端 ...
- 推荐系统系列(四):PNN理论与实践
背景 上一篇文章介绍了FNN [2],在FM的基础上引入了DNN对特征进行高阶组合提高模型表现.但FNN并不是完美的,针对FNN的缺点上交与UCL于2016年联合提出一种新的改进模型PNN(Produ ...
- Linux之防火墙iptables
一.检查iptables服务状态 1.首先检查iptables服务的状态 [root@bogon ~]# service iptables status iptables: Firewall is n ...
- Linux 下运行 class 文件
1. 基本命令 java -cp searchDir classFile -cp searchDir : 指定从该目录搜索 class 文件 classFile :类路径(不包含 .class ...
- SSRF和XSS-filter_var(), preg_match() 和 parse_url()绕过学习
0x01:url标准的灵活性导致绕过filter_var与parse_url进行ssrf filter_var() (PHP 5 >= 5.2.0, PHP 7) filter_var — 使用 ...
- TCP输入 之 tcp_queue_rcv
tcp_queue_rcv用于将接收到的skb加入到接收队列receive_queue中,首先会调用tcp_try_coalesce进行分段合并到队列中最后一个skb的尝试,若失败则调用__skb_q ...
- 【黑马Javaweb】1.1Junit单元测试
黑马第一天学习 今日内容 Junit单元测试: 测试分类: Junit使用:白盒测试 今日内容 1.1Junit单元测试 1.2.反射 1.3.注解 Junit单元测试: 测试分类: 1.黑盒测试:不 ...
- linux目录简介说明