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)角色:负责保存好备忘录。不能对备忘录的内容进行操作或检查。
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对象
*/
不得不说,这个真麻烦。
我是菜鸟,我在路上。