您的位置:首页 > 其它

设计模式学习笔记——备忘录模式

2014-07-19 17:15 429 查看
原文:http://blog.csdn.net/hackerain/article/details/7563246

定义:

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

备忘录模式主要是对某个对象的状态的备份,备份的主要是对象当前的属性值,即成员变量的值,成员变量可以有多个,而且可以备份同一个对象的多种不同状态,即同一个对象可以同时有多个备份,先来看最简单的情况,即一个对象只有一个成员变量:

其通用类图为:



源代码如下:

[java] view
plaincopy

/*

* 单属性的备忘录模式,本类即是需要被备份的类

*/

public class Originator {

private String state;

public String getState() {

return state;

}

public void setState(String state) {

this.state = state;

}

public void changeState(){

this.state="heart hurt...";

}

public Memento createMemento(){

return new Memento(this.state);

}

public void restoreMemento(Memento memento){

this.setState(memento.getState());

}

}

[java] view
plaincopy

/*

* 备忘录类

*/

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;

}

}

[java] view
plaincopy

/*

* 备忘录管理类

*/

public class Caretaker {

private Memento memento;

public Memento getMemento() {

return memento;

}

public void setMemento(Memento memento) {

this.memento = memento;

}

}

[java] view
plaincopy

public class Client {

public static void main(String[] args) {

Originator originator=new Originator();

Caretaker caretaker=new Caretaker();

originator.setState("happy...");

System.out.println(originator.getState());

//保存当前的状态

caretaker.setMemento(originator.createMemento());

//状态改变

originator.changeState();

System.out.println(originator.getState());

//恢复状态

originator.restoreMemento(caretaker.getMemento());

System.out.println(originator.getState());

}

}

要是遇到多属性怎么办?要是一个对象不仅仅只需要备份一次,而需要备份很多次,又该如何呢?看下面改进源码:

[java] view
plaincopy

/*

* 多属性的备忘录模式

*/

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;

}

//将本对象的各个属性以HashMap类型保存到Memento对象中

public Memento createMemento(){

return new Memento(BeanUtils.beanToHash(this));

}

//从Memento对象中取出保存的对象的属性状态,并赋值给本对象的各个属性

public void restoreMemento(Memento memento){

BeanUtils.hashToBean(this, memento.getStateMap());

}

public String toString(){

return this.state1+" "+this.state2+" "+this.state3;

}

}

[java] view
plaincopy

//Memento的成员变量为一个HashMap类型的,是为了保存源目标对象的各个属性的名和值

public class Memento {

private HashMap<String,Object> stateMap;

public Memento(HashMap<String, Object> stateMap) {

this.stateMap = stateMap;

}

public HashMap<String, Object> getStateMap() {

return stateMap;

}

public void setStateMap(HashMap<String, Object> stateMap) {

this.stateMap = stateMap;

}

}

[java] view
plaincopy

/**

* 多备份备份,即可以备份一个对象的多种状态,

* 注意这种多备份,不要用于备份特别频繁的地方,容易出现内存溢出,

* 或者增加Map的上限,防止出现内存的泄漏。

*/

public class Caretaker {

private HashMap<String,Memento> mementos=new HashMap<String,Memento>();

public Memento getMemento(String key) {

return mementos.get(key);

}

public void setMemento(String key, Memento memento) {

this.mementos.put(key, memento);

}

}

[java] view
plaincopy

/*

* 使用此类是为了操作方便

*/

public class BeanUtils {

/*

* 把bean的所有属性及数值放到HashMap中

*/

public static HashMap<String,Object> beanToHash(Object bean){

HashMap<String,Object> result=new HashMap<String,Object>();

try{

BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());//获得bean描述

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;

}

//把HashMap的值放到bean中

public static void hashToBean(Object bean, HashMap<String,Object> result){

try{

BeanInfo beanInfo=Introspector.getBeanInfo(bean.getClass());//获得bean描述

PropertyDescriptor[] descriptors=beanInfo.getPropertyDescriptors();//获得属性描述

//遍历所有属性

for(PropertyDescriptor des : descriptors){

String fieldName=des.getName();//属性名

if(result.containsKey(fieldName)){

Method setter=des.getWriteMethod();//写属性的方法

setter.invoke(bean, new Object[]{result.get(fieldName)});//将值写入到属性中

}

}

}catch(Exception e){

}

}

}

[java] view
plaincopy

public class Client {

public static void main(String[] args) {

Originator originator=new Originator();

Caretaker caretaker=new Caretaker();

originator.setState1("brave");

originator.setState2("responsibility");

originator.setState3("clever");

System.out.println("001: "+originator.toString());

caretaker.setMemento("001",originator.createMemento());//创建第一个备份

originator.setState1("weak");

originator.setState2("lazy");

originator.setState3("temper");

System.out.println("002: "+originator.toString());

caretaker.setMemento("002",originator.createMemento());//创建第二个备份

originator.setState1("self-reflection");

originator.setState2("get up early");

originator.setState3("go to libyary everyday");

System.out.println("003: "+originator.toString());

caretaker.setMemento("003",originator.createMemento());//创建第三个备份

originator.restoreMemento(caretaker.getMemento("001"));//恢复

System.out.println("now: "+originator.toString());

}

}

多个属性,多个备份嘛,那就把备忘录类和备忘录管理类换成集合类型就可以了。但是这种做法要特别注意内存溢出的问题,不要在备份频繁的地方使用这种备忘录模式,可以设置Map的上限,来防止内存泄露。

另外,备份还可以有另外一种实现方式,备份嘛,自然会联想到复制,也就联想到原型模型了,可以使用克隆的方法来实现备忘录,如下源码:

[java] view
plaincopy

/*

* 通过克隆对象的方式实现备忘录

*/

public class Originator implements Cloneable{

private String state;

public String getState() {

return state;

}

public void setState(String state) {

this.state = state;

}

public void changeState(){

this.state="heart hurt...";

}

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;

}

}

[java] view
plaincopy

public class Caretaker {

private Originator originator;

public Originator getMemento() {

return originator;

}

public void setMemento(Originator originator) {

this.originator = originator;

}

}

还有,如果需要想要增加安全性,即备份的东西是不可以随便改变的,除了原发起对象之外,其他的对象是都不可访问这个备忘录的,我个人现在浅薄的观点认为,只要在备忘录类中不实现那个setMemento()方法,不就可以避免这个类的对象被修改了吗?反正这个方法没有被用到,不知道这样做对不对,《设计模式之禅》的作者有他的做法,即把备忘录类作为原发起对象的一个内部类,这样就可以实现只有发起对象可以访问这个备忘录类了,再让这个内部类实现一个空的接口,让其可以在外和其他类建立关联关系,其实现代码如下:

[java] view
plaincopy

public interface IMemento {

}

[java] view
plaincopy

/*

* 使用内部类,增强安全性

*/

public class Originator {

private String state;

public String getState() {

return state;

}

public void setState(String state) {

this.state = state;

}

public void changeState(){

this.state="heart hurt...";

}

public Memento 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;

}

}

}

[java] view
plaincopy

//在原发起类的外部使用备忘录类的空接口建立和其他对象的关联

public class Caretaker {

private IMemento memento;

public IMemento getMemento() {

return memento;

}

public void setMemento(IMemento memento) {

this.memento = memento;

}

}

备忘录模式的使用场景:

1、需要保存和恢复数据的相关状态场景

2、数据库连接的事务管理就是用的备忘录模式
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息