小A:“那‘月光寶盒’備忘錄模式有哪些組成部分?”
大B:“1、備忘錄(Memento)角色:備忘錄角色存儲‘備忘發起角色’的內部狀態。‘備忘發起角色’根據需要決定備忘錄角色存儲‘備忘發起角色’的哪些內部狀態。爲了防止‘備忘發起角色’以外的其他對象訪問備忘錄。 備忘錄實際上有兩個接口,‘備忘錄管理者角色’只能看到備忘錄提供的窄接口——對於備忘錄角色中存放的屬性是不可見的。‘備忘發起角色’則能夠看到一個寬接口——能夠得到自己放入備忘錄角色中屬性。2、備忘發起(Originator)角色:‘備忘發起角色’創建一個備忘錄,用以記錄當前時刻它的內部狀態。在需要時使用備忘錄恢復內部狀態。3、備忘錄管理者(Caretaker)角色:負責保存好備忘錄。不能對備忘錄的內容進行操作或檢查。稱它爲寬接口;而另一個則可以只是一個標示,稱它爲窄接口。 備忘錄角色要實現這兩個接口類。這樣對於‘備忘發起角色’採用寬接口進行訪問,而對於其他的角色或者對象則採用窄接口進行訪問。這種實現比較簡單,但是需要人爲的進行規範約束——而這往往是沒有力度的。第二種方法便很好的解決了第一種的缺陷:採用內部類來控制訪問權限。將備忘錄角色作爲‘備忘發起角色’的一個私有內部類。好處就不詳細解釋了,看看代碼就明白了。下面的代碼是一個完整的備忘錄模式的程序。它便採用了第二種方法來實現備忘錄模式。還有一點值得指出的是,在下面的代碼中,對於客戶程序來說‘備忘錄管理者角色’是不可見的,這樣簡化了客戶程序使用備忘錄模式的難度。下面採用‘備忘發起角色’來調用訪問‘備忘錄管理者角色’,也可以參考外觀模式在客戶程序與備忘錄角色之間添加一個外觀角色。”
classOriginator{
//這個是要保存的狀態
privateintstate=90;
//保持一個“備忘錄管理者角色”的對象
privateCaretakerc=newCaretaker();
//讀取備忘錄角色以恢復以前的狀態
publicvoidsetMemento(){
Mementomemento=(Memento)c.getMemento();
state=memento.getState();
System.out.println(“thestateis”+state+“now”);
}
//創建一個備忘錄角色,並將當前狀態屬性存入,託給“備忘錄管理者角色”存放。
publicvoidcreateMemento(){
c.saveMemento(newMemento(state));
}
//thisisotherbusinessmethods……
//theymaybemodifytheattributestate
publicvoidmodifyState4Test(intm){
state=m;
System.out.println(“thestateis”+state+“now”);
}
//作爲私有內部類的備忘錄角色,它實現了窄接口,可以看到在第二種方法中寬接口已經不再需要
//注意:裡面的屬性和方法都是私有的
privateclassMementoimplementsMementoIF{
privateintstate;
privateMemento(intstate){
this.state=state;
}
privateintgetState(){
returnstate;
}
}
}
//測試代碼——客戶程序
publicclassTestInnerClass{
publicstaticvoidmain(String[]args){
Originatoro=newOriginator();
o.createMemento();
o.modifyState4Test(80);
o.setMemento();
}
}
//窄接口
interfaceMementoIF{}
//“備忘錄管理者角色”
classCaretaker{
privateMementoIFm;
publicvoidsaveMemento(MementoIFm){
this.m=m;
}
publicMementoIFgetMemento(){
returnm;
}
}
大B:“第三種方式是不太推薦使用的:使用clone方法來簡化備忘錄模式。由於Java提供了clone機制,這使得複製一個對象變得輕鬆起來。使用了clone機制的備忘錄模式,備忘錄角色基本可以省略了,而且可以很好的保持對象的封裝。但是在爲你的類實現clone方法時要慎重!在上面的代碼中,簡單的模擬了備忘錄模式的整個流程。在實際應用中,往往需要保存大量‘備忘發起角色’的歷史狀態。這時就要對‘備忘錄管理者角色’進行改造,最簡單的方式就是採用容器來按照順序存放備忘錄角色。這樣就可以很好的實現undo、redo功能了。”