设计模式——备忘录模式

备忘录模式

定义

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

备忘录模式(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;
	}

	@Override
	protected 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;
	}

	@Override
	public 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);
}

结果

在这里插入图片描述

参考书籍

秦小波《设计模式之禅》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加把劲骑士RideOn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值