设计模式_备忘录模式

Mememento Pattern 
     Without violating encapsulation,capture and externalize an object's internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样就可将该对象恢复到原先保存的状态。)
又称Tonken模式

不认识的单词
violating  违反,亵渎
externalize  给…以外形,使客观化,使具体化 
internal  国内的;内部的;体内的;内心的 

1) 备忘录(Memento)角色:备忘录角色存储“备忘发起角色”的内部状态。“备忘发起角色”根据需要决定备忘录角色存储“备忘发起角色”的哪些内部状态。为了防止“备忘发起角色”以外的其他对象访问备忘录。备忘录实际上有两个接口,“备忘录管理者角色”只能看到备忘录提供的窄接口——对于备忘录角色中存放的属性是不可见的。“备忘发起角色”则能够看到一个宽接口——能够得到自己放入备忘录角色中属性。 

2) 备忘发起(Originator)角色:“备忘发起角色”创建一个备忘录,用以记录当前时刻它的内部状态。在需要时使用备忘录恢复内部状态。

3) 备忘录管理者(Caretaker)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。


带目的去看问题往往会很清晰。

public class Originator {
 private String state="";
 
 public void changeState(){
  this.state="maybe not so good";
 }
 public String getState() {
  return state;
 }
 public void setState(String state) {
  this.state = state;
 }
 
 public Memento createMemote(){
  return new Memento(this.state);
 }
 
 public void restoreMomoto(Memento _memoto){
  this.setState(_memoto.getState());
 }
}

public class Memento {
 private String state="";
 
 public Memento(String _state) {
  this.state=_state;
 }
 public String getState() {
  return this.state;
 }
 
}

public class Caretaker {
 private Memento memoto;
 
 public Memento getMemoto(){
  return memoto;
 }
 
 public void setMemoto(Memento memoto){
  this.memoto=memoto;
 }
}

public class Client {
 
 public static void main(String[] args) {
  Originator orihinator=new Originator();	 //发起人
  Caretaker caretaker=new Caretaker();  	 //备忘录管理者,不能改变内容
  orihinator.setState("It's so good");	 //设置状态
  caretaker.setMemoto(orihinator.createMemote());  //创建备份点
  orihinator.changeState();                        //改变状态
  orihinator.restoreMomoto(caretaker.getMemoto());  //恢复至备份点
 }
}
/*
 *  这样看来,Memoto备忘录就是一个JavaBean,存储数据。
 *  为什么还需要Caretaker? 为了不违背迪米特原则,只和朋友类交流,备份类不是朋友,系统需求只是在某个点创建备份
 *  所以备忘录管理类就出来了,看起来又是一个JavaBean,在备忘录之上又包装了一层,麻烦,但是这样结构清晰了许多
 *  发起人不需要和备份类交流
 *  
 *  例子 :  网站   -> 浏览器 -> cookie -> 网站...  
 *  用户发送一个请求给服务器,服务器会创建一个相对应的session对象,发送数据至浏览器并设置cookie,在一段时间里
 *  用户可以访问服务器,服务器可以根据sessionID来判断是哪个session对象
 */

多状态备份 
在发起人中成员属性多个状态,在备忘录类就是一个hashMap,存放多个状态,通过BeanUtils工具类注入参数和返回参数。 

public class BeanUtils {
//把bean的所有属性及数值放入到Hashmap中
public static HashMap<String,Object> backupProp(Object bean){
HashMap<String,Object> result = new HashMap<String,Object>();
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<String,Object> 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();
}
}
}

多备份备忘录 
    检查点,在caretaker中稍微做点修改

public class Caretaker {
//容纳备忘录的容器
private HashMap<String,Memento> memMap = new HashMap<String,Memento>();
public Memento getMemento(String idx) {
return memMap.get(idx);
}
public void setMemento(String idx,Memento memento) {
this.memMap.put(idx, memento);
}
}


权限设计
    只能由发起人访问,私有的内置类,但是备忘录管理需要关联,这个时候就可以使用空接口,但是访问属性可以通过反射和修改数据,这是一种设计方法,叫做"双接口设计",将Memento类内置于Originator ,这样只能由Originator 访问了。

双接口设计
双接口设计,我们的一个类可以实现多个接口, 在系统设计时,如果考虑对象的安全问题,则可以提供两个接口,一个是业务的正常接口, 实现必要的业务逻辑,叫做宽接口;另外一个接口是一个空接口,什么方法都没有,其目的 是提供给子系统外的模块访问,比如容器对象,这个叫做窄接口,由于窄接口中没有提供任 何操纵数据的方法,因此相对来说比较安全。

因为一个一个写例子的确看起来很简单,我修改了大约40分钟把三者功能合一,其中有些地方确实该的不好,但是至少明白了其中的道。

public class Originator { 
 private String state1="";  //状态1
 private String state2="";  //状态2
 private String state3="";  //状态3
 
 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的构造方法注入startMap中
  * @return
  */
 public Memento createMemote(){
  return new Memento(BeanUtils.backupProp(this));
 }
 
 /**
  * 因为权限的关系,外部不可能传进来一个Memento对象,这里就将就的使用了一个HashMap集合
  * 通过反射,将该实例的所有属性值替换成hashMap中的值
  * @param map
  */
 public void restoreMomoto(HashMap<String,Object> map){
  BeanUtils.restoreProp(this, map);
 }
 
 /**
  * 内置类,别人不可能访问
  * 实现一个空接口是为了与外部关联,在实际的开发中一个业务接口,一个空接口用于其它子模块,通过反射就可以为所欲为了
  * @author suibian
  */
 private class Memento implements IMemento{
  private HashMap<String,Object> startMap;
   
  public Memento(HashMap<String,Object> _startMap) {
   this.startMap=_startMap;
  }
  public HashMap<String, Object> getStartMap() {
   return startMap;
  }
  public void setStartMap(HashMap<String, Object> startMap) {
   this.startMap = startMap;
  }
 }
 /*测试使用
  */
 @Override
 public String toString() {
  return "state1="+state1+"\t state2="+state2+"\t state3="+state3;
 }
}

public class Caretaker {
 
 private HashMap<String,IMemento> map=new HashMap<String,IMemento>();
 
 public IMemento getMemoto(String idx){
  return map.get(idx);
 }
 /**
  * 多个备份
  * @param idx  时间锉
  * @param memoto 备份接口
  */
 public void setMemoto(String idx,IMemento memoto){
  this.map.put(idx, memoto);
 }
}

public class Client {
 @SuppressWarnings("unchecked")
 public static void main(String[] args) {
  Originator ori1 = new Originator();
  Caretaker caretaker = new Caretaker();
  ori1.setState1("备份1状态1");
  ori1.setState2("备份1状态2");
  ori1.setState3("备份1状态3");
  caretaker.setMemoto("第一个", ori1.createMemote());
  Originator ori2 = new Originator();
  ori2.setState1("备份2状态1");
  ori2.setState2("备份2状态2");
  ori2.setState3("备份2状态3");
  caretaker.setMemoto("第二个", ori2.createMemote());
  System.out.println(ori1.toString());
  System.out.println(ori2.toString());
  System.out.println("----------------------备份完毕----------------------------");
 
  ori1.setState1("备份1状态1改变了");
  System.out.println("改变后的:"+ori1.toString());
 
  System.out.println("-----------------------开始恢复-----------------------------");
 
  IMemento memento = caretaker.getMemoto("第一个");  //从备份管理获取备份实例
  //得到是Memento中的属性startMap再转化成能注参的map对象
  HashMap<String,Object> data=(HashMap<String, Object>) BeanUtils.backupProp(memento).get("startMap");
 
  for(Entry<String, Object> e:data.entrySet()){
   System.out.println("这是存起来的备份数据:"+e.getKey()+"\t"+e.getValue());
  }
  //将备份实例中的数据data通过反射注入到ori1中去
  ori1.restoreMomoto(data);
  System.out.println("------------------恢复完后的数据-------------------------");
  System.out.println(ori1.toString());
  System.out.println(ori2.toString());
 }
}
/*错误地方
 *通过 BeanUtils.backupProp(memento)获取的是对象Memento类的所有属性值
 *而Originator恢复需要的是一个hashMap通过反射注参,一个属性名对应一个值,而上面获取的hashMap是一个属性名,一个hashMap对象
 *因为权限只能由Originator来恢复,所以修改了restoreMomoto()方法,不传Memento,而传一个HashMap对象
 */

不得不说,这个真麻烦。 


我是菜鸟,我在路上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值