设计模式学习笔记(二十二:备忘录模式)
1.1概述
? ? 在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣以后就可以將該對象恢復到原先保存的狀態。這就是備忘錄模式的定義。
? 對象的狀態依賴于它的變量的取值情況,對象在程序運行期間的各個時刻可能具有不同的狀態。在某些應用中,程序可能需要使用一種合理的方式來保存對象在某一時刻的狀態,以便在需要時,對象能恢復到原先保存的狀態。在備忘錄模式中,稱需要保存狀態的對象為“原發者”,稱負責保存原發者狀態的對象為“備忘錄”,稱負責管理備忘錄的對象為“負責人”。備忘錄模式要求原發者可以訪問備忘錄中的細節,即可以訪問備忘錄中的數據,以便恢復原發者的狀態,而負責人只能保存和得到備忘錄,但訪問備忘錄中的數據受到一定的限制。備忘錄模式使原發者可以將自己的狀態暴露給備忘錄,但其他對象想要獲得備忘錄中的數據會受到一定的限制,這就保證了原發者暴露內部數據的同時,又保證了數據的封裝性。另外,經過精心設計的備忘錄在保存原發者狀態時,可能只需要保存原發者的部分變量即可,也就是備忘錄通過保存原發者狀態中最本質的數據,就能使原發者根據此備忘錄中的數據恢復原始狀態。
? 例如,對于一個游戲軟件,該游戲可能需要經過許多關卡才能最后成功,那么該游戲應當提供保存“游戲關卡”的功能,使游戲玩者在成功完成游戲的某一個關卡之后,保存當前的游戲狀態,當玩到下一個關卡失敗時,可以選擇游戲從上一次保存的狀態開始,即從上一次成功后的關卡開始,而不是再從第1關開始。
?
?
1.2模式的結構
備忘錄模式包括以下三種角色:
(1)原發者(Originator):需要在某個時刻保存其狀態的對象。原發者負責創建備忘錄,比如使用createMemento()方法創建一個備忘錄,然后原發者使用該備忘錄記錄自己的狀態。當原發者需要恢復到某個狀態時,它通過獲取備忘錄中相應的數據來恢復到那一時刻的狀態,比如原發者調用restoreFromMemento(Memento mem)方法,并通過參數mem指定備忘錄恢復狀態。
(2)備忘錄(Memento):負責存儲原發者狀態的對象,創建備忘錄的類和創建原發者的類在同一個包中,該類提供的訪問數據的方法都是友好方法,使得只有原發者在用一個包中的類的實例才可以訪問備忘錄中的數據。
(3)負責人(Caretaker):負責管理保存備忘錄中的對象。負責人如果不和原發者在同一個包中就不能對備忘錄中的內容進行修改或讀取。如果需要將備忘錄保存到磁盤,負責人可以使用對象流將備忘錄寫入文件。
備忘錄模式結構的類圖如下所示:
?
?
圖一:備忘錄模式的類圖
?
?
1.3備忘錄模式的優點
(1)備忘錄模式使用備忘錄可以把原發者的內部狀態保存起來,使只有很“親密的”對象可以訪問備忘錄中的數據。
(2)備忘錄模式強調了類設計單一責任原則,即將狀態的刻畫和保存分開。
?
?
1.4適合使用備忘錄模式的情景
(1)必須保存一個對象在某一時刻的全部或部分狀態,以便在需要時恢復該對象先前的狀態。
(2)一個對象不想通過提供public權限的,諸如getXXX()的方法讓其他對象得到自己的內部狀態。
?
?
?
1.5備忘錄模式的使用
使用備忘錄模式來設計一個GUI程序,主要功能如下:
- 程序的窗體中有一個標簽組件,用戶在標簽上單擊鼠標左鍵可以在標簽上隨機顯示一個漢字。但標簽上只保留最后一次單擊左鍵所顯示的漢字。
- 程序提供undo操作。當用戶在標簽上單擊鼠標右鍵時,將取消用戶最近一次單擊鼠標左鍵所產生的操作效果,即將標簽上的漢字恢復為上一次單擊鼠標左鍵所得到的漢字。用戶多次單擊鼠標右鍵來依次取消單擊鼠標左鍵所產生的操作效果。(對于功能2,在實例代碼中出現了一點問題,即點擊鼠標右鍵沒有任何反應,問題待解決...)
??首先看一下本實例構建框架具體類和1.2模式的結構中類圖的對應關系,如下圖所示:
?
(1)原發者(Originator)和備忘錄(Memento)
本程序中,原發者是UnicodeLable類的實例,UnicodeLable類是java.swing 包中JLabel類的子類,包含Integer對象,該對象中的int值代表一個漢字在Unicode表中的位置。備忘錄是UnicodeLable類的內部類,因此,只有UnicodeLable類的實例可以訪問備忘錄中的數據,其他類無法獲得備忘錄中的數據,UnicodeLable類代碼如下:
package com.liuzhen.twenty_two_memento;import java.awt.Font; import java.awt.event.InputEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent;import javax.swing.JLabel; import javax.swing.SwingConstants;@SuppressWarnings("serial") public class UnicodeLable extends JLabel{private Integer m;public UnicodeLable(){setFont(new Font("宋體",Font.BOLD,100));setHorizontalAlignment(SwingConstants.CENTER);m = new Integer(19968);setText(""+(char)m.intValue());addMouseListener(new MouseAdapter(){public void mouseReleased(MouseEvent e){if(e.getModifiers() == InputEvent.BUTTON1_MASK){ //釋放左鍵m = (int)(Math.random()*1000+19968);setText(""+(char)m.intValue());}}});}public Memento createMemento(){Memento mem = new Memento();mem.setState(m);return mem;}public void restoreFromMemento(Memento mem){m = mem.getState();if(m != null)setText(""+(char)m.intValue());}public class Memento{ //Memento是UnicodeLabel中的內部類private Integer m;private void setState(Integer m){this.m = m;}private Integer getState(){return m;}} }?
(2)負責人(Caretaker)
對于本問題,負責人是Caretaker類。Caretaker類使用一個堆棧來存放備忘錄,當用戶需要undo操作時,從堆棧彈出最近一次的備忘錄給用戶,用戶用該備忘錄恢復原發者的狀態,當堆棧為空時,用戶不能進行ubdo操作。Caretaker類的代碼如下:
package com.liuzhen.twenty_two_memento;import java.util.Stack;public class Caretaker {Stack<UnicodeLable.Memento> stack;Caretaker(){stack = new Stack<UnicodeLable.Memento>();}public UnicodeLable.Memento getMemento(){if(!(stack.isEmpty())){UnicodeLable.Memento memento = stack.pop();return memento;}elsereturn null;}public void saveMemento(UnicodeLable.Memento memento){stack.push(memento);} }?
(3)具體使用
?通過Twenty_twoApplication類來具體實現上述相關類和接口,來實現備忘錄模式的運用,其代碼如下:
package com.liuzhen.twenty_two_memento;import java.awt.BorderLayout; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener;import javax.swing.JFrame; import javax.swing.JLabel;@SuppressWarnings("serial") public class Twenty_twoApplication extends JFrame implements MouseListener{UnicodeLable label;Caretaker caretaker; //負責人 Twenty_twoApplication(){label = new UnicodeLable();label.addMouseListener(this);add(new JLabel("單擊左鍵顯示一個漢字,單擊右鍵撤銷單擊左鍵的操作效果"),BorderLayout.NORTH);add(label,BorderLayout.CENTER);caretaker = new Caretaker(); //創建負責人 }public void mousePressed(MouseEvent e){if(e.getModifiers() == InputEvent.BUTTON1_DOWN_MASK){ //按下左鍵caretaker.saveMemento(label.createMemento()); //保存備忘錄 }if(e.getModifiers() == InputEvent.BUTTON3_DOWN_MASK){ //按下右鍵UnicodeLable.Memento memento = caretaker.getMemento(); //得到備忘錄if(memento != null){label.restoreFromMemento(memento); //使用備忘錄恢復狀態 }}}public void mouseReleased(MouseEvent e){}public void mouseEntered(MouseEvent e){}public void mouseExited(MouseEvent e){}public void mouseClicked(MouseEvent e){}public static void main(String[] args){Twenty_twoApplication win = new Twenty_twoApplication();win.setBounds(10,10,300,300);win.setVisible(true);win.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);} }?
?
運行結果截圖:
?
?
參考資料:
? ? ??1.Java設計模式/耿祥義,張躍平著.——北京:清華大學出版社,2009.5
總結
以上是生活随笔為你收集整理的设计模式学习笔记(二十二:备忘录模式)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MeshLab中进行点云配准
- 下一篇: 设计模式学习笔记(总结篇:模式分类)