23种设计模式(15):备忘录模式

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

类型:行为类

类图:

我们在编程的时候,常常须要保存对象的中间状态,当须要的时候,能够恢复到这个状态。比方,我们使用Eclipse进行编程时,假如编写失误(比如不小心误删除了几行代码),我们希望返回删除前的状态,便能够使用Ctrl+Z来进行返回。这时我们便能够使用备忘录模式来实现。

备忘录模式的结构

  • 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
  • 备忘录:负责存储发起人对象的内部状态,在须要的时候提供发起人须要的内部状态。
  • 管理角色:对备忘录进行管理,保存和提供备忘录。

通用代码实现

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());
	}
}

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;
	}
}
class Caretaker {
	private Memento memento;
	public Memento getMemento(){
		return memento;
	}
	public void setMemento(Memento memento){
		this.memento = memento;
	}
}
public class Client {
	public static void main(String[] args){
		Originator originator = new Originator();
		originator.setState("状态1");
		System.out.println("初始状态:"+originator.getState());
		Caretaker caretaker = new Caretaker();
		caretaker.setMemento(originator.createMemento());
		originator.setState("状态2");
		System.out.println("改变后状态:"+originator.getState());
		originator.restoreMemento(caretaker.getMemento());
		System.out.println("恢复后状态:"+originator.getState());
	}
}

代码演示了一个单状态单备份的样例,逻辑很easy:Originator类中的state变量须要备份,以便在须要的时候恢复;Memento类中,也有一个state变量,用来存储Originator类中state变量的暂时状态;而Caretaker类就是用来管理备忘录类的,用来向备忘录对象中写入状态或者取回状态。

 

多状态多备份备忘录

通用代码演示的样例中,Originator类仅仅有一个state变量须要备份,而通常情况下,发起人角色一般是一个javaBean,对象中须要备份的变量不止一个,须要备份的状态也不止一个,这就是多状态多备份备忘录。实现备忘录的方法非常多,备忘录模式有非常多变形和处理方式,像通用代码那样的方式一般不会用到,多数情况下的备忘录模式,是多状态多备份的。事实上实现多状态多备份也非常easy,最经常使用的方法是,我们在Memento中添加?一个Map容器来存储全部的状态,在Caretaker类中相同使用一个Map容器才存储全部的备份。以下我们给出一个多状态多备份的样例:

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());
	}
	public String toString(){
		return "state1="+state1+"state2="+state2+"state3="+state3;
	}
}
class Memento {
	private Map<String, Object> stateMap;

	public Memento(Map<String, Object> map){
		this.stateMap = map;
	}

	public Map<String, Object> getStateMap() {
		return stateMap;
	}

	public void setStateMap(Map<String, Object> stateMap) {
		this.stateMap = stateMap;
	}
}
class BeanUtils {
	public static Map<String, Object> backupProp(Object bean){
		Map<String, Object> result = new HashMap<String, Object>();
		try{
			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){
			e.printStackTrace();
		}
		return result;
	}

	public static void restoreProp(Object bean, Map<String, Object> propMap){
		try {
			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) {
			e.printStackTrace();
		}
	}
}
class Caretaker {
	private Map<String, Memento> memMap = new HashMap<String, Memento>();
	public Memento getMemento(String index){
		return memMap.get(index);
	}

	public void setMemento(String index, Memento memento){
		this.memMap.put(index, memento);
	}
}
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("001",ori.createMemento());
		ori.setState1("软件");
		ori.setState2("架构");
		ori.setState3("优秀");
		System.out.println("===改动后状态===\n"+ori);

		ori.restoreMemento(caretaker.getMemento("001"));
		System.out.println("===恢复后状态===\n"+ori);
	}
}

备忘录模式的优缺点和适用场景

备忘录模式的长处有:

  • 当发起人角色中的状态改变时,有可能这是个错误的改变,我们使用备忘录模式就能够把这个错误的改变还原。
  • 备份的状态是保存在发起人角色之外的,这样,发起人角色就不须要对各个备份的状态进行管理。

备忘录模式的缺点:

  • 在实际应用中,备忘录模式都是多状态和多备份的,发起人角色的状态须要存储到备忘录对象中,对资源的消耗是比較严重的。

假设有须要提供回滚操作的需求,使用备忘录模式很适合,比方jdbc的事务操作,文本编辑器的Ctrl+Z恢复等。

23种设计模式(15):备忘录模式

时间: 2024-11-05 01:09:25

23种设计模式(15):备忘录模式的相关文章

【Unity与23种设计模式】备忘录模式(Memento)

GoF中定义: "在不违反封装的原则下,获取一个对象的内部状态并保留在外部,让对象可以在日后恢复到原先保留时的状态." 对于一些需要存储的数据,比如历史最高分 当与得分减分系统写入一个类时,违反了单一职责原则 最好是做一个SaveData的类单独存储或获取 而当使用一个单独的类时,又必须将数据public向外公开 这就将游戏置于危险的境地,甚至是方便了外挂横行 针对此矛盾局势 备忘录模式便解决了这一问题 备忘录模式可以描述为: 在不增加各个游戏系统类成员的"存取"方

23种设计模式之备忘录模式(Memento)

备忘录模式确保在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态.备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤. 优点: 1)保持封装的完整. 2)简化了返回到初始状态所需的操作. 使用场景: 1)必须保存对象状态的快照,这样以后就可以恢复状态. 2)使用直接接口来获得状态可能会公开对象的实现细节,从而破坏对象的封装性. Memento 模式

Java经典23种设计模式之创造型模式(二)

本文记录5种创造型模式的剩下两种:建造者模式(Builder).原型模式(PROTOTYPE). 一.建造者模式(别名:生成者模式) 将复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示.一个完整的建造者模式包含以下几个概念: 1.产品类 Product public class Person { private String head; private String body; private String foot; public String getHead() { ret

JAVA开发的23种设计模式之 --- 桥接模式

桥接模式 概述:将抽象部分与他的实现部分分离,这样抽象化与实现化解耦,使他们可以独立的变化.如何实现解耦的呢,就是通过提供抽象化和实现化之间的桥接结构.    应用场景        实现系统可能有多个角度分类,每一种角度都可能变化.    解释:桥接模式将继承模式转化成关联关系,他降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量.    理解抽象化,实现化,解耦        抽象化:将复杂物体的一个或几个共同的特性抽出去而只注意其他特性的行动或过程.在java面向对象中抽象化就

Java经典23种设计模式之创造型模式(一)

设计模式被称为程序猿的内功,之前零零散散的看过一大部分,但自己么有总结过.故此次在这里总结下.值得一提的是,设计模式并不是Java所特有.由于一直搞Android.这里就用Java为载体.最经典的设计模式有23种,分三个大类型: 创建型模式(5) .结构型模式(7).行为型模式(11),5 + 7 +11 = 23.网上一搜也都是一大把了,这里不过个人作的记录.本文记录创造型模式里的工厂方法(Factory Method).抽象工厂(Abstract Factory).单例模式这三种.力求透彻.

23种设计模式(19)---Command模式

命令(Command)模式属于对象的行为模式[GOF95].命令模式又称为行动(Action)模式或交易(Transaction)模式.命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象. 每一个命令都是一个操作:请求的一方发出请求要求执行一个操作:接收的一方收到请求,并执行操作.命令模式允许请求的一方和接收的

23种设计模式----行为型模式

行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为.由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性.它包含以下 11 种模式: 模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以

23种设计模式之代理模式(Proxy)

代理模式是一种对象结构型模式,可为某个对象提供一个代理,并由代理对象控制对原对象的引用.代理模式能够协调调用者和被调用者,能够在一定程度上降低系统的耦合度,其缺点是请求的处理速度会变慢,并且实现代理模式需要额外的工作. 优点: 1)远程代理可以隐藏对象位于不同的地址空间的事实. 2)虚拟代理可以执行优化操作,例如根据需要创建一个对象. 使用场景:需要比简单的指针更灵活.更全面的对象引用. Proxy 模式

23种设计模式之原型模式(Prototype)

在系统开发过程中,有时候有些对象需要被频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后通过复制这个原型对象的办法,创建出更多同类型的对象.原型模式是一种对象创建型模式,用原型实例制定创建对象的种类,并且通过复制这些原型创建新的对象.原型模式又可分为两种:浅克隆和深克隆.浅克隆仅仅复制所考虑的对象,而不复制它所引用的对象,也就是其中的成员对象并不复制:深克隆除了对象本身被复制外,对象包含的引用也被复制,即成员对象也被复制. 优点: 1)可以在运行时添加或删除产品. 2)通过改

23种设计模式(6)--Bridge模式

面向对象的设计原则:高内聚.低耦合 软件重构原则:小步快跑------抽取的思想(抽取函数.抽取类.抽取接口):对扩展开放.对修改封闭 设计模式分类如下: Bridge模式主要是解决多维度问题,什么意思呢?类似于n*m这个公式,n种抽象的接口,m种具体的实现,最多可以有n*m种组合方式. 下面这篇文章对Bridge模式讲解的通俗易懂,于是转了过来. 学习设计模式也有一段时间了,今天就把我整理的一篇课程和大家分享,有不妥之处欢迎指出. 生活中的一个例子: 就拿汽车在路上行驶的来说.即有小汽车又有公