本文是對設(shè)計模式之禪一書中備忘錄模式一章的總結(jié)與整理迂求。
1. 定義
在不破壞封裝性的前提下惶我,捕獲一個對象的內(nèi)部狀態(tài)专挪,并在該對象之外保存這個狀態(tài)接箫,這樣以后就可以將對象恢復(fù)到原先保存的的狀態(tài)攒读,我就不明月了,為啥這些大師總喜歡將定義寫的這么復(fù)雜呢辛友,我看極有可能是自己的水平?jīng)]那個份上薄扁,這個定義寫成接地氣的話就是“在操作一個對象前,把這個對象臨時保存一份废累,以便將來恢復(fù)邓梅,相當(dāng)于是后悔藥∫乇酰”通用類圖如下:
通用類圖.png
- Originator 發(fā)起人角色:記錄當(dāng)前時刻的內(nèi)部狀態(tài)日缨,負(fù)責(zé)定義哪些屬于備份的狀態(tài),負(fù)責(zé)創(chuàng)建和恢復(fù)備忘錄數(shù)據(jù)掖看。
- Memento 備忘錄角色:負(fù)責(zé)存儲Originator發(fā)起人對象的內(nèi)部狀態(tài) 匣距,在需要的時候提供發(fā)起人需要的內(nèi)部狀態(tài)
- Caretaker備忘錄管理員角色:對備忘錄進(jìn)行管理、保存和提供備忘錄
書中說這類標(biāo)準(zhǔn)的模很難在項目中遇到哎壳,基本者是變換后的處理方式毅待,所以此處不再提供示例。
2. 擴(kuò)展
第一種:Clone方式的備忘錄
通過復(fù)制的方式產(chǎn)生一個對象的內(nèi)部狀態(tài)归榕,類圖如下:
類圖1.png
發(fā)起人:
public class Originator implements Cloneable {
private Originator backup;
//內(nèi)部狀態(tài)
private String state="";
public String getState(){
return this.state;
}
public void setState(String _state){
this.state=_state;
}
//創(chuàng)建備忘錄
public void createMemento(){
this.backup = this.clone();
}
//克隆當(dāng)前對象
@Override
protected Originator clone(){
try {
return (Originator)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
//恢復(fù)備忘錄
public void restoreMemento(){
this.setState(this.backup.getState());
}
}
備忘錄管理者
public class Caretaker {
//發(fā)起人對象
private Originator originator;
public Originator getOriginator(){
return this.originator;
}
public void setOriginator(Originator _originator){
this.originator=_originator;
}
}
場景類:
public class Client {
public static void main(String[] strings) {
Originator originator = new Originator();
//初始狀態(tài)
originator.setState("初始狀態(tài).....");
System.out.println("初始狀態(tài)是:"+originator.getState());
//備份
originator.createMemento();
//修改狀態(tài)
originator.setState("修改狀態(tài)....");
System.out.println("修改后的狀態(tài)為:"+originator.getState());
//恢復(fù)狀態(tài)
originator.restoreMemento();
System.out.println("恢復(fù)后的狀態(tài)為:"+originator.getState());
}
}
運行結(jié)果如下:
結(jié)果1.png
第二類:多狀態(tài)的備忘錄模式
第一種情況中只一種狀態(tài)尸红,如果有多種狀態(tài)就采用這種方式,通用類圖如下:
類圖2.png
再來看代碼,發(fā)起人:
public class Originator implements Cloneable {
//內(nèi)部狀態(tài)
private String state1="";
public String getState2() {
return state2;
}
public void setState2(String state2) {
this.state2 = state2;
}
public String getState1() {
return state1;
}
public void setState1(String state1) {
this.state1 = state1;
}
public String getState3() {
return state3;
}
public void setState3(String state3) {
this.state3 = state3;
}
private String state2="";
private String state3="";
//創(chuàng)建備忘錄
public Memento createMemento(){
return new Memento(BeanUtils.backupProps(this));
}
//恢復(fù)備忘錄
public void restoreMemento(Memento _memento){
BeanUtils.restoreProps(this,_memento.getStateMap());
}
//toString
@Override
public String toString() {
return "state1="+state1+",state2="+state2+",state3="+state3;
}
}
備忘錄如下:
public class Memento {
public HashMap<String, Object> getStateMap() {
return stateMap;
}
public void setStateMap(HashMap<String, Object> stateMap) {
this.stateMap = stateMap;
}
//接愛HashMap作為狀態(tài)
private HashMap<String,Object> stateMap;
//接受一個對象,建立備份
public Memento(HashMap<String,Object> map){
this.stateMap=map;
}
}
工具類:
public class BeanUtils {
public static HashMap<String,Object> backupProps(Object bean){
HashMap<String,Object> result=new HashMap<>();
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 ex){
}
return result;
}
public static void restoreProps(Object bean,HashMap<String,Object> propMap){
//獲取屬性描述
BeanInfo beanInfo= null;
try {
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();
}
}
}
備忘錄管理員角色:
public class Caretaker {
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
private Memento memento;
}
場景類:
public class Client {
public static void main(String[] strings){
Originator originator=new Originator();
originator.setState1("中國");
originator.setState2("強(qiáng)盛");
originator.setState3("繁榮");
System.out.println("初始狀態(tài):"+originator.toString());
//創(chuàng)建一個備忘錄
Caretaker caretaker=new Caretaker();
caretaker.setMemento(originator.createMemento());
originator.setState1("軟件");
originator.setState3("架構(gòu)");
originator.setState2("優(yōu)秀");
System.out.println("后狀態(tài):"+originator.toString());
originator.restoreMemento(caretaker.getMemento());
System.out.println("原狀態(tài):"+originator.toString());
}
}
運行結(jié)果如下:
結(jié)果2.png
第三種情況是多備份外里,就是有多個備份的情況怎爵,再看備忘錄管理員角色如下:
public class Caretaker {
//創(chuàng)建一個容器
private HashMap<String, Memento> mementoHashMap = new HashMap<>();
public Memento getMemento(String idx) {
return mementoHashMap.get(idx);
}
public void setMementoHashMap(String idx, Memento memento) {
this.mementoHashMap.put(idx, memento);
}
}
``
再來看場景類如下:
```java
public class Client {
public static void main(String[] strings){
Originator originator=new Originator();
originator.setState1("中國");
originator.setState2("強(qiáng)盛");
originator.setState3("繁榮");
System.out.println("初始狀態(tài):"+originator.toString());
//創(chuàng)建一個備忘錄
Caretaker caretaker=new Caretaker();
//創(chuàng)建兩個備份
caretaker.setMementoHashMap("001",originator.createMemento());
caretaker.setMementoHashMap("002",originator.createMemento());
originator.setState1("軟件");
originator.setState3("架構(gòu)");
originator.setState2("優(yōu)秀");
System.out.println("后狀態(tài):"+originator.toString());
//恢復(fù)一份備份
originator.restoreMemento(caretaker.getMemento("001"));
System.out.println("原狀態(tài):"+originator.toString());
}
}
結(jié)果就不上了。
3.結(jié)論
備忘錄模式盅蝗,嚴(yán)格來講是一個比較簡單的模式鳖链,可以把它理解成一個臨時表的作用,當(dāng)臨時表使风科,只是它的變化形式比較多而已撒轮,但我認(rèn)為記住以上的這三種情況就足夠用了。
注:本方代碼段比較多贼穆,莫怪题山。