常用设计模式有23中,分为:

创建型模式(主要用于创建对象)

1、单例模式    2、工厂方法模式    3、抽象工厂模式    4、建造者模式     5、原型模式 
行为型模式 (主要用于描述对象或类是怎样交互和怎样分配职责)

1、模板方法模式  2、中介者模式  3、命令模式    4、责任链模式   5、策略模式   6、迭代器模式

7、观察者模式      8、备忘录模式   9、访问者模式   10、状态模式   11、解释器模式

结构型模式(主要用于处理类或对象的组合)

1、代理模式  2、装饰模式   3、适配器模式   4、组合模式   5、外观模式(门面模式)   6、享元模式    7、桥梁模式

备忘录模式  
备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让“后悔药”在程序的世界中真实可行,其定义如下:  
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态  
三个角色。
● Originator发起人角色
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
● Memento备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态。
● Caretaker备忘录管理员角色

对备忘录进行管理、保存和提供备忘录。

备忘录模式的通用代码
发起人角色,如代码清单24-8所示。
<span style="font-size:18px;">代码清单24-8 发起人角色
public class Originator {
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public Memento createMemento(){
return new Memento(this.state);
}
//恢复一个备忘录
public void restoreMemento(Memento _memento){
this.setState(_memento.getState());
}
}</span>

我们再来看备忘录角色,如代码清单24-9所示。
<span style="font-size:18px;">代码清单24-9 备忘录角色
public class Memento {
//发起人的内部状态
private String state = "";
//构造函数传递参数
public Memento(String _state){
this.state = _state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}</span>

这是一个简单的JavaBean,

备忘录管理者也是一个简单的JavaBean,如代码清单24-10所示。
<span style="font-size:18px;">代码清单24-10 备忘录管理员角色
public class Caretaker {
//备忘录对象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}</span>

我们来看场景类如何调用,如代码清单24-11所示。
<span style="font-size:18px;">public class Client {
public static void main(String[] args) {
//定义出发起人
Originator originator = new Originator();
//定义出备忘录管理员
Caretaker caretaker = new Caretaker();
//创建一个备忘录
caretaker.setMemento(originator.createMemento());
//恢复一个备忘录
originator.restoreMemento(caretaker.getMemento());
}
}</span>

备忘录模式就是这么简单,真正使用备忘录模式的时候可比这复杂得多。  

由于备忘录模式有太多的变形和处理方式,每种方式都有它自己的优点和缺点,标准的备忘录模式很难在项目中遇到,基本上都有一些变换处理方式。因此,我们在使用备忘录模式时主要了解如何应用以及需要注意哪些事项就成了。  
使用场景  
● 需要保存和恢复数据的相关状态场景。  
● 提供一个可回滚(rollback)的操作;比如Word中的CTRL+Z组合键,IE浏览器中的后退按钮,文件管理器上的backspace键等。  
● 需要监控的副本场景中。  
● 数据库连接的事务管理就是用的备忘录模式  
注意事项  
● 备忘录的生命期  
备忘录创建出来就要在“最近”的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。  
● 备忘录的性能  
不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中)  
备忘录模式的扩展 
1)clone方式的备忘录  
从类图上看,发起人角色融合了发起人角色和备忘录角色,具有双重功效,如代码清单24-12所示。
<span style="font-size:18px;">代码清单24-12 融合备忘录的发起人角色
public class Originator implements Cloneable{
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public Originator createMemento(){
return this.clone();
}
//恢复一个备忘录
public void restoreMemento(Originator _originator){
this.setState(_originator.getState());
}
//克隆当前对象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}</span>
增加了clone方法,产生了一个备份对象,需要使用的时候再还原,
管理员角色,如代码清单24-13所示。
<span style="font-size:18px;">代码清单24-13 备忘录管理员角色
public class Caretaker {
//发起人对象
private Originator originator;
public Originator getOriginator() {
return originator;
}
public void setOriginator(Originator originator) {
this.originator = originator;
}
}</span>

没什么太大变化,只是备忘录角色转换成了发起人角色,还是一个简单的JavaBean。
我们来想想这种模式是不是还可以简化?要管理员角色干什么?就是为了管理备忘录角色,现在连备忘录角色都被合并了,还留着它干吗?我们想办法把它也精简掉,如代码清单24-14所示。
<span style="font-size:18px;">代码清单24-14 发起人自主备份和恢复
public class Originator implements Cloneable{
private Originator backup;
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public void createMemento(){
this.backup = this.clone();
}
//恢复一个备忘录
public void restoreMemento(){
//在进行恢复前应该进行断言,防止空指针
this.setState(this.backup.getState());
}
//克隆当前对象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}</span>

可能你要发问了,这和备忘录模式的定义不相符,它定义是“在该对象之外保存这个状态”,而你却把这个状态保存在了发起人内部。
是的,设计模式定义的诞生比Java的出世略早,它没有想到Java程序是这么有活力,有远见,而且在面向对象的设计中,即使把一个类封装在另一个类中也是可以做到的,何况一个小小的对象复制,这是它的设计模式完全没有预见到的,我们把它弥补回来。
再来看看Client是如何调用的,如代码清单24-15所示。
<span style="font-size:18px;">代码清单24-15 场景类
public class Client {
public static void main(String[] args) {
//定义发起人
Originator originator = new Originator();
//建立初始状态
originator.setState("初始状态...");
System.out.println("初始状态是:"+originator.getState());
//建立备份
originator.createMemento();
//修改状态
originator.setState("修改后的状态...");
System.out.println("修改后状态是:"+originator.getState());
//恢复原有状态
originator.restoreMemento();
System.out.println("恢复后状态是:"+originator.getState());
}
}  </span>

程序精简了很多,而且高层模块的依赖也减少了 ,考虑一下原型模式深拷贝和浅拷贝的问题,在复杂的场景下它会让你的程序逻辑异常混乱, 
注意 使用Clone方式的备忘录模式,可以使用在比较简单的场景或者比较单一的场景中,尽量不要与其他的对象产生严重的耦合关系。  
2)多状态的备忘录模式  
还是比较简单的类图,增加了一个BeanUtils类,其中
backupProp是把发起人的所有属性值转换到HashMap中,方便备忘录角色存储;
restoreProp方法则是把HashMap中的值返回到发起人角色中。
为什么要使用HashMap,直接使用Originator对象的拷贝不是一个很好的方法吗?
可以这样做,你就破坏了发起人的通用性,你在做恢复动作的时候需要对该对象进行多次赋值操作,也容易产生错误  
代码清单  
<span style="font-size:18px;">代码清单24-16 发起人角色
public class Originator {
//内部状态
private String state1 = "";
private String state2 = "";
private String state3 = "";
public String getState1() {
return state1;
}
public void setState1(String state1) {
this.state1 = state1;
}
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
public String getState3() {
return state3;
}
public void setState3(String state3) {
this.state3 = state3;
}
//创建一个备忘录
public Memento createMemento(){
return new Memento(BeanUtils.backupProp(this));
}
//恢复一个备忘录
public void restoreMemento(Memento _memento){
BeanUtils.restoreProp(this, _memento.getStateMap());
}
//增加一个toString方法
@Override
public String toString(){
return "state1=" +state1+"\nstat2="+state2+"\nstate3="+state3;
}
}</span>
我们再来看BeanUtils工具类,如代码清单24-17所示。
<span style="font-size:18px;">代码清单24-17 BeanUtils工具类
public class BeanUtils {
//把bean的所有属性及数值放入到Hashmap中
public static HashMap backupProp(Object bean){
HashMap result = new HashMap();
try {
//获得Bean描述
BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());
//获得属性描述
PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();
//遍历所有属性
for(PropertyDescriptor des:descriptors){
//属性名称
String fieldName = des.getName();
//读取属性的方法
Method getter = des.getReadMethod();
//读取属性值
Object fieldValue=getter.invoke(bean,new Object[]{});
if(!fieldName.equalsIgnoreCase("class")){
result.put(fieldName, fieldValue);
}
}
} catch (Exception e) {
//异常处理
}
return result;
}
//把HashMap的值返回到bean中
public static void restoreProp(Object bean,HashMap propMap){
try {
//获得Bean描述
BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());
//获得属性描述
PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
//遍历所有属性
for(PropertyDescriptor des:descriptors){
//属性名称
String fieldName = des.getName();
//如果有这个属性
if(propMap.containsKey(fieldName)){
//写属性的方法
Method setter = des.getWriteMethod();
setter.invoke(bean, new Object[]{propMap.get(fieldName)});
}
}
} catch (Exception e) {
//异常处理
System.out.println("shit");
e.printStackTrace();
}
}
}</span>

该类大家在项目中会经常用到,可以作为参考使用。类似的功能有很多工具已经提供,比如Spring、Apache工具集commons等,大家也可以直接使用。
我们再来看备忘录角色,如代码清单24-18所示。
<span style="font-size:18px;">代码清单24-18 备忘录角色
public class Memento {
//接受HashMap作为状态
private HashMap stateMap;
//接受一个对象,建立一个备份
public Memento(HashMap map){
this.stateMap = map;
}
public HashMap getStateMap() {
return stateMap;
}
public void setStateMap(HashMap stateMap) {
this.stateMap = stateMap;
}
}</span>

我们再编写一个场景类,看看我们的成果是否正确,如代码清单24-19所示。
<span style="font-size:18px;">代码清单24-19 场景类
public class Client {
public static void main(String[] args) {
//定义出发起人
Originator ori = new Originator();
//定义出备忘录管理员
Caretaker caretaker = new Caretaker();
//初始化
ori.setState1("中国");
ori.setState2("强盛");
ori.setState3("繁荣");
System.out.println("===初始化状态===\n"+ori);
//创建一个备忘录
caretaker.setMemento(ori.createMemento());
//修改状态值
ori.setState1("软件");
ori.setState2("架构");
ori.setState3("优秀");
System.out.println("\n===修改后状态===\n"+ori);
//恢复一个备忘录
ori.restoreMemento(caretaker.getMemento());
System.out.println("\n===恢复后状态===\n"+ori);
}
}  </span>

注意 如果要设计一个在运行期决定备份状态的框架,则建议采用AOP框架来实现,避免采用动态代理无谓地增加程序逻辑复杂性。  
3)多备份的备忘录  
检查点(Check Point),也就是你在备份的时候做的戳记,系统级的备份一般是时间戳,  
我们只要把通用代码中的Caretaker管理员稍做修改就可以了,如代码清单24-20所示。
<span style="font-size:18px;">代码清单24-20 备忘录管理员
public class Caretaker {
//容纳备忘录的容器
private HashMap memMap = new HashMap();
public Memento getMemento(String idx) {
return memMap.get(idx);
}
public void setMemento(String idx,Memento memento) {
this.memMap.put(idx, memento);
}
}</span>

把容纳备忘录的容器修改为Map类型就可以了,场景类也稍做改动,如代码清单24-21所示。
<span style="font-size:18px;">代码清单24-21 场景类
public class Client {
public static void main(String[] args) {
//定义出发起人
Originator originator = new Originator();
//定义出备忘录管理员
Caretaker caretaker = new Caretaker();
//创建两个备忘录
caretaker.setMemento("001",originator.createMemento());
caretaker.setMemento("002",originator.createMemento());
//恢复一个指定标记的备忘录
originator.restoreMemento(caretaker.getMemento("001"));
}
}  </span>

注意 内存溢出问题,该备份一旦产生就装入内存,没有任何销毁的意向,这是非常危险的。因此,在系统设计时,要严格限定备忘录的创建,建议增加Map的上限,否则系统很
容易产生内存溢出情况。 
 
4)封装得更好一点  
 一个备份的数据是完全、绝对不能修改的,它保证数据的洁净,避免数据污染而使备份失去意义
备份是不能被篡改的,也就是说需要缩小备份出的备忘录的阅读权限,保证只能是发起人可读就成  
这也是比较简单的,建立一个空接口IMemento——什么方法属性都没有的接口,然后在发起人Originator类中建立一个内置类(也叫做类中类)Memento实现IMemento接口,同时也实现自己的业务逻辑,如代码清单24-22所示。  
<span style="font-size:18px;">Originator {
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public IMemento createMemento(){
return new Memento(this.state);
}
//恢复一个备忘录
public void restoreMemento(IMemento _memento){
this.setState(((Memento)_memento).getState());
}
//内置类
private class Memento implements IMemento{
//发起人的内部状态
private String state = "";
//构造函数传递参数
private Memento(String _state){
this.state = _state;
}
private String getState() {
return state;
}
private void setState(String state) {
this.state = state;
}
}
}</span>
内置类Memento全部是private的访问权限,也就是说除了发起人外,别人休想访问到,
那如果要产生关联关系又应如何处理呢?
通过接口!别忘记了我们还有一个空接口是公共的访问权限,
<span style="font-size:18px;">代码清单24-23 备忘录的空接口
public interface IMemento {
}</span>

我们再来看管理者,如代码清单24-24所示。

<span style="font-size:18px;">代码清单24-24 备忘录管理者
public class Caretaker {
//备忘录对象
private IMemento memento;
public IMemento getMemento() {
return memento;
}
public void setMemento(IMemento memento) {
this.memento = memento;
}
}</span>

全部通过接口访问,这当然没有问题,如果你想访问它的属性那是肯定不行的。
但是安全是相对的,没有绝对的安全,可以使用refelect反射修改Memento的数据。
在这里我们使用了一个新的设计方法:双接口设计,我们的一个类可以实现多个接口,在系统设计时,如果考虑对象的安全问题,则可以提供两个接口,
一个是业务的正常接口,实现必要的业务逻辑,叫做宽接口;
另外一个接口是一个空接口,什么方法都没有,其目的是提供给子系统外的模块访问,比如容器对象,这个叫做窄接口,由于窄接口中没有提供任何操纵数据的方法,因此相对来说比较安全。  

设计模式笔记--备忘录模式相关推荐

  1. 设计模式:备忘录模式(Memento)

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  2. java备忘录模式应用场景_图解Java设计模式之备忘录模式

    图解Java设计模式之备忘录模式 游戏角色状态恢复问题 游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss后攻击力和防御力下降,从备忘录对象恢复到大战前的状态. ...

  3. java之备忘录模式,java设计模式之备忘录模式

    java设计模式之备忘录模式 代码无错便是优? 简介: 在不破坏封装性的前提下 , 捕获一个对象的内部状态,并在该对象之外保存这个状态 , 这样以后就可以将该对象恢复到原先保存的状态 . 备忘录模式就 ...

  4. 设计模式笔记——代理模式

    设计模式笔记--代理模式 代理模式介绍 代理模式通常是介于请求方和提供方的一个中介系统,请求方是发送请求的一方,提供方是根据请求提供相应资源的一方 Web中的代理服务器就是一个例子,客户端向代理服务器 ...

  5. Alamps学习设计模式之备忘录模式(笑话:劫个色OR抢个鸡蛋版)

    //设计模式之备忘录模式(月光宝盒版),只要喊菠萝菠萝蜜,就能返回事件的原点.//至尊宝//爱你一万年 class LoverWords {// 模拟Memento     private Strin ...

  6. 设计模式之备忘录模式 转载

    https://juejin.im/post/59c8eb6951882564c5164c5f 设计模式之备忘录模式 备忘录模式 介绍 是一种行为模式 用于保存对象当前状态,并在之后恢复到此状态(后悔 ...

  7. java备忘录模式 类图,Android编程设计模式之备忘录模式详解

    本文实例讲述了Android编程设计模式之备忘录模式.分享给大家供大家参考,具体如下: 一.介绍 备忘录模式是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态,这有点像我们平时 ...

  8. 设计模式读书笔记-----备忘录模式

    个人比较喜欢玩单机游戏,什么仙剑.古剑.鬼泣.使命召唤.三国无双等等一系列的游戏我都玩过(现在期待凡人修仙传),对于这些游戏除了剧情好.场面大.爽快之外,还可以随时存档,等到下次想玩了又可以从刚开始的 ...

  9. 【设计模式】—— 备忘录模式Memento

    前言:[模式总览]----------by xingoo 模式意图 这个模式主要是想通过一个对象来记录对象的某种状态,这样有利于在其他需要的场合进行恢复. 该模式还有跟多可以扩展的地方,比如可以记录多 ...

最新文章

  1. Ubuntu E: Unable to locate package错误解决办法
  2. Lucene 对文档打分的规则整理记录
  3. 【java排序】 归并排序算法、堆排序算法
  4. 第3关:HDFS-JAVA接口之上传文件
  5. TP5.x——update更新成功但是返回是0
  6. javascript-mqtt
  7. 10个调试和排错的小建议
  8. 数字逻辑电路课程设计之数字电子时钟
  9. JavaScript基础
  10. 如何优雅地下载PDF格式知网硕博论文?
  11. Unity-TA 成长之路(二)内置渲染管线-官方篇
  12. 2022年山东省安全员B证考试练习题及在线模拟考试
  13. 紫猫插件-文件读写(1-13)
  14. 关于浏览器及其内核以及什么是浏览器兼容性
  15. excel单元格内容拆分_EXCEL批量拆分单元格,也可以这么快
  16. CSS动画效果(animation属性)解析
  17. 计算机组成原理——存储器系统
  18. mysql对表中添加属性_MySQL数据库增删改字段(属性)
  19. 定制联想笔记本一键恢复内容
  20. 【leetcode-Python】-Dynamic Programming -309. Best Time to Buy and Sell Stock with Cooldown

热门文章

  1. Windows目录结构、移动、复制、搜索文件及文件夹等
  2. 每个人心里都有一座巴别塔-The dogs of babel巴别塔之犬
  3. 服务器任务栏运行程序不见了,Win7系统任务栏上正在运行的程序图标不见了怎么办...
  4. python 实现自动化办公 人工统计考勤与电脑匹配
  5. IOS项目新手引导页图片适配方案
  6. IDEA 使用Spring Boot框架实现hello world
  7. python calu_使用Python检查变量是否介于两个值之间
  8. SCT2601TVBR、LMR16006XDDCR、MP2456GJ-Z参数
  9. QQ注册页面(完整版)
  10. C语言打开微信提示找不到文件,电脑提示系统找不到指定文件怎么办?