设计模式——备忘录模式

备忘录模式

定义

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,之后就可以将该对象恢复到原先保存的状态。

备忘录模式(Memento Pattern)是一种弥补缺陷的模式,能够在失败的操作后读档

应用场景

  1. 需要保存和恢复数据的场景
  2. 需要提供一个可回滚(rollback)的操作
  3. 需要监控的副本场景
  4. 数据库连接的事务管理就是使用备忘录模式

备忘录模式的角色

  1. Originator发起人。记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据
  2. Memento备忘录角色。负责Originator当前状态的快照,之后需要时提供数据回滚
  3. Caretaker备忘录管理员。管理Memento角色,将发起人对备忘录角色的使用权交给管理员。

非备忘录模式

小结

  1. 没有备忘录角色、备忘录管理员,数据的备份靠new一个实例来记录
  2. new出的实例是高层模型控制,不符合封装的特点,应该将其定义容纳起来。

发起人

public class Boy {private String state;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}
}

入口类方法

/*** 非备忘录模式*/
private static void notMemento() {Boy boy = new Boy();// 初始化状态boy.setState("心情很好");System.out.println("=====男孩现在的状态=====" + boy.getState());// 记录状态Boy backup = new Boy();backup.setState(boy.getState());// 男孩去追女孩,状态改变boy.changeState();System.out.println("=====男孩追女孩之后的状态=====" + boy.getState());// 追女孩失败,恢复原状boy.setState(backup.getState());System.out.println("=====男孩恢复后的状态=====" + boy.getState());
}

结果

在这里插入图片描述

备忘录模式

小结

  1. 添加了备忘录角色Memento和备忘录管理员Caretaker
  2. 发起人Originator在某个时刻发起备份,管理员负责管理备份。
  3. 发起人发起回滚时,管理员调出先前记录的备忘录,提供给发起人以回滚。

UML图

在这里插入图片描述

发起人——Originator

/*** 发起人*/
public class Man {private String state;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}/*** 保存一个备份** @return 备忘录*/public Memento createMemento() {return new Memento(this.state);}/*** 恢复一个备份*/public void restoreMemento(Memento memento) {this.state = memento.getState();}
}

备忘录角色——Memento

/*** 备忘录角色*/
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;}
}

备忘录管理员——Caretaker

/*** 备忘录管理员*/
public class Caretaker {/*** 备忘录角色*/private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}

入口类方法

/*** 加入备忘录管理员*/
private static void mementoByCaretaker() {Man man = new Man();// 创建备忘录管理员Caretaker caretaker = new Caretaker();// 初始化状态man.setState("心情很好");System.out.println("=====男孩现在的状态=====" + man.getState());// 记录状态caretaker.setMemento(man.createMemento());// 男孩去追女孩,状态改变man.changeState();System.out.println("=====男孩追女孩之后的状态=====" + man.getState());// 追女孩失败,恢复原状man.restoreMemento(caretaker.getMemento());System.out.println("=====男孩恢复后的状态=====" + man.getState());
}

结果

在这里插入图片描述

拓展:clone方式的备忘录

小结

  1. 结合原型模式clone的特点,在发起人内部重写clone()方法
  2. 发起人除了自身的状态外,还维护一个副本对象,当发起人发起备份时,会将clone出的对象复制给副本对象
  3. 使用clone方式的备忘录可以舍弃备忘录对象Memento和备忘录管理员Caretaker

发起人——Originator

/*** 融合备忘录的发起人角色*/
public class Originator implements Cloneable {/*** 内部状态*/private String state;/*** 自主备份状态*/private Originator backup;/*** 认识女孩后状态的变化*/public void changeState() {setState("心情可能很不好");}public String getState() {return state;}public void setState(String state) {this.state = state;}/*** 保存一个备份*/public void createMemento() {try {this.backup = this.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}}/*** 恢复一个备份*/public void restoreMemento() {this.state = Optional.of(this.backup).orElseGet(Originator::new).state;}@Overrideprotected Originator clone() throws CloneNotSupportedException {return (Originator) super.clone();}
}

入口类方法

/*** 使用克隆完成备忘*/
private static void mementoByClone() {// 定义发起人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());
}

结果

在这里插入图片描述

拓展:多状态的备忘录模式

小结

  1. 多状态下会引入map来备份bean对象的fieldName和fieldValue
  2. 使用到java.beans下的反射工具Introspectors获取到bean的getter、setter方法获取bean的属性
  3. Clone方式的备忘录,适合在单一场景下使用,不适合在与其他对象有强耦合的场景(因为没有备忘录角色和备忘录管理员的参与)。
  4. 多状态的备忘录模式则分工明确,但是使用反射来装载bean会降低一些效率

发起人

/*** 多状态 发起人角色*/
public class OriginatorMulState {private String state1;private String state2;private String state3;/*** 创建一个备忘录** @return 备忘录*/public MementoMul createMemento() {return new MementoMul(BeanUtils.backupProp(this));}public void restoreMemento(MementoMul mementoMul) {BeanUtils.restoreProp(this, mementoMul.getStateMap());}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;}@Overridepublic String toString() {return "OriginatorMulState{" +"state1='" + state1 + '\'' +", state2='" + state2 + '\'' +", state3='" + state3 + '\'' +'}';}
}

备忘录角色

/*** 备忘录角色 多状态*/
public class MementoMul {private Map<String, Object> stateMap;public MementoMul(Map<String, Object> stateMap) {this.stateMap = stateMap;}public Map<String, Object> getStateMap() {return stateMap;}public void setStateMap(Map<String, Object> stateMap) {this.stateMap = stateMap;}
}

备忘录管理员

/*** 多状态 备忘录管理员*/
public class CaretakerMulState {/*** 备忘录角色*/private MementoMul memento;public MementoMul getMemento() {return memento;}public void setMemento(MementoMul memento) {this.memento = memento;}
}

Bean工具类

/*** Bean工具类*/
public class BeanUtils {/*** 把bean的所有属性放入到Map中** @param bean 待备忘的对象* @return 备忘对象的map*/public static <T> Map<String, Object> backupProp(T bean) {Map<String, Object> map = new HashMap<>();try {BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor descriptor : descriptors) {String fieldName = descriptor.getName();Method getter = descriptor.getReadMethod();Object fieldValue = getter.invoke(bean);if (!fieldName.equalsIgnoreCase("class")) {map.put(fieldName, fieldValue);}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}return map;}public static <T> void restoreProp(T bean, Map<String, Object> map) {try {BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();for (PropertyDescriptor descriptor : descriptors) {String fieldName = descriptor.getName();if (map.containsKey(fieldName)) {Method setter = descriptor.getWriteMethod();setter.invoke(bean, map.get(fieldName));}}} catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {e.printStackTrace();}}
}

入口类方法

/*** 多状态 备忘录*/
private static void mulState() {OriginatorMulState originatorMulState = new OriginatorMulState();CaretakerMulState caretaker = new CaretakerMulState();originatorMulState.setState1("Take");originatorMulState.setState2("it");originatorMulState.setState3("boy");System.out.println("初始化状态:" + originatorMulState);caretaker.setMemento(originatorMulState.createMemento());// 修改状态originatorMulState.setState1("玩");originatorMulState.setState2("游");originatorMulState.setState3("戏");System.out.println("修改后的状态:" + originatorMulState);// 恢复状态originatorMulState.restoreMemento(caretaker.getMemento());System.out.println("恢复后的状态:" + originatorMulState);
}

结果

在这里插入图片描述

参考书籍

秦小波《设计模式之禅》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/22616.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

敏捷测试自动化

目录 前言&#xff1a; 敏捷宣言 敏捷软件开发VS传统方法 敏捷云服务 对什么进行自动化&#xff1f; 测试自动化的技巧 关于敏捷测试和自动化测试的关键信息 前言&#xff1a; 敏捷测试自动化是在敏捷开发环境中使用自动化工具和技术来支持测试活动的一种方法。它旨在提…

4G 网络跟 5G 的区别

4G网络和5G网络是两种不同的移动通信技术&#xff0c;它们在数据传输速度、延迟、连接密度和网络容量等方面存在一些区别。以下是它们之间的主要区别&#xff1a; 1. 速度&#xff1a;5G网络的速度比4G网络更快。5G网络具备更广的频段和更高的频率&#xff0c;能够提供更大的带…

【数据结构---排序】庖丁解牛式剖析常见的排序算法

排序算法 一、常见的排序算法二、常见排序算法的实现1. 直接插入排序2. 希尔排序3. 直接选择排序4. 堆排序5. 冒泡排序6. 快速排序6.1 递归实现快速排序思路一、hoare 版本思路二、挖坑法思路三、前后指针法 6.2 非递归实现快速排序 7. 归并排序7.1 归并排序的递归实现7.2 归并…

KuiperInfer深度学习推理框架环境配置-Ubuntu 22.04

KuiperInfer项目地址 Github项目地址 B站课程地址 安装Armadillo 官网&#xff1a;Armadillo官网 介绍&#xff1a;Armadillo C Library是一种C的线性代数库&#xff0c;包含一些矩阵和向量的运算&#xff0c;可以选用高效的LAPACK和BLAS进行加速。 矩阵相关计算的文档&…

发布项目管理生产的时候出现界面滚动不了

新的项目管理发布后出现页面鼠标上下滚动不了的问题&#xff0c;比较奇怪的是&#xff0c;本地开发没有问题&#xff0c;但发布生产后就出现问题了。 因为这次增加的组件比较多&#xff0c;相应的样式也比较多&#xff0c;所以那先分几个部分处理 1、先新加的几个组件是否有冲…

Unity VR 开发教程 OpenXR+XR Interaction Toolkit(九)根据不同物体匹配对应的抓取手势

文章目录 &#x1f4d5;教程说明&#x1f4d5;前置准备&#x1f4d5;HandData 脚本存储手部数据&#x1f4d5;制作预设手势&#x1f4d5;手势匹配脚本 GrabHandPose⭐完整代码⭐需要保存的数据⭐得知什么时候开始抓取和取消抓取⭐将手势数据赋予手部模型⭐平滑变化手势⭐开始抓…

Spark编程-共享变量(广播变量和累加器)

共享变量是什么 Spark中的两个重要抽象一个是RDD&#xff0c;另一个就是共享变量。 在默认情况下&#xff0c;当Spark在集群的多个不同节点的多个任务上并行运行一个函数时&#xff0c;它会把函数中涉及到的每个变量&#xff0c;在每个任务上都生成一个副本。 但是&…

单轴机器人的结构与特点

单轴机器人是由马达驱动的移动平台&#xff0c;由滚珠螺杆和 U型线性滑轨导引构成&#xff0c;其滑座同时为滚珠螺杆的驱动螺帽及线性滑轨的导引滑块&#xff0c;可用半导体、光电、交通运输业、环保节能产业、精密工具机、机械产业、智慧自动化、生技医疗上。 相对于传统的模组…

Python Web框架 Flask 安装、使用

Python Web框架 Flask 安装 安装 Flask 框架 首先需要安装 Flask 框架, 可以通过以下命令安装: [rootlocalhost web]# pip3 install Flask Collecting FlaskDownloading Flask-2.0.3-py3-none-any.whl (95 kB)|██████████████████████████████…

中国8K摄像机厂家加入国际广电设备制造商协会IABM

近日&#xff0c;BOSMA博冠正式成为国际广电设备制造商协会IABM会员&#xff0c;标志着中国8K摄像机厂家BOSMA博冠在广播电视超高清前端采集领域受到全球广电权威机构认可&#xff0c;进一步推动国产品牌在全球市场竞争中提升品牌知名度、加强行业影响力。 IABM创立于1976年&am…

Spring MVC 注解实现

注解描述 注解描述Controller用于标记在一个类上&#xff0c;使用它标记的类就是一个SpringMVC Controller 对象&#xff0c;分发处理器将会扫描使用了该注解的类的方法&#xff0c;并检测该方法是否使用了RequestMapping 注解。Controller 只是定义了一个控制器类&#xff0c…

Docker 网络

一、背景&#xff1a; 想一下这个问题&#xff0c;容器和容器之间是否可以通过网络正常通信&#xff1f;宿主机和容器是否可以通信&#xff1f;如果可以通信&#xff0c;那为什么可以通信。如果不可以通信&#xff0c;如何让他们之间通信。接下来就详细的讲解下 docker 的网络。…