描述 對象的狀態可以定義為在特定的時間點對象的屬性值
備忘錄模式(Memento Pattern)應用於保存和跟蹤對象的狀態
以便於必要的時候可以把對象恢復到以前的狀態
它很像恢復操作
備忘錄模式(Memento Pattern)可以在不暴露對象的內部結構的情況下完成這樣的功能
需要獲取以前狀態的對象就是指發起者(Originator)
當客戶需要保存發起者的狀態時
客戶需要發起者的當前狀態
發起者存貯所有保持它狀態的屬性到一個獨立的對象
這個對象就是備忘錄(紀念
記憶)Memento
把備忘錄(Memento)對象返回給客戶
備忘錄(Memento)對象可以看作在給定的時間點包含另一個對象內部狀態的對象
備忘錄(Memento)對象必須向除了發起者以外的所有對象隱藏發起者變量的值
當發起者允許備忘錄(Memento)對象訪問它的內部狀態時
備忘錄(Memento)對象應該被設計為對其他對象采取訪問限制的對象
當客戶需要把發起者的狀態恢復到以前的狀態時
它只是簡單的把備忘錄(Memento)對象返回給發起者
發起者使用包含在備忘錄(Memento)對象中的狀態信息
恢復自己到備忘錄(Memento)對象中保存的狀態
例子 數據轉化(Data conversion)總是那些涉及到從遺留系統轉化到應用新技術的系統不可缺少的一部分
讓我們假定一個需要把客戶數據從文本文件移植到關系型數據庫中的類似應用程序
在將客戶數據發送給數據庫以前
要對客戶紀錄進行驗證
現實中
客戶紀錄需要包括很多屬性
但是為了簡單
讓我們假定每一個客戶紀錄只有三個屬性??first name
last name和credit card number
驗證過程也很簡單
只要last name不為空而且credit card number(信用卡號)僅有
的數字組成
當發現一個無效的客戶記錄時
驗證過程需要停止
提示用戶修正數據並重新開始
在這個時間點上
數據轉化(Data conversion)過程的狀態需要保存在一個備忘錄(Memento)對象內部
當用戶重新開始驗證過程時
數據裝化過程從保存在備忘錄(Memento)對象中的狀態開始
驗證過程從它停止的地方恢復
而不是從原數據起點重新開始
通常
備忘錄(Memento)對象既可以保存在內存中也可以保存在持久介質上
在這個應用中
當應用被打斷以後
狀態需要保存
而且當應用再次運行的時候需要恢復
因此
在這種情況下
不適於把備忘錄(Memento)對象保存在內存中
而是需要保存在持久介質上
不是直接把合法的客戶紀錄插入到關系數據庫中
應用程序而是生成一個由SQL插入語句組成的文本文件
執行這些SQL語句可以把數據插入到數據庫中
讓我們為這個驗證過程設計不同的組件
DataConverter(發起者) DataConverter類(圖
和Listing
)是數據轉化過程的實現
![](http://tw.wingwit.com/Article/UploadFiles/201311/2013112319544454.jpg)
Figure : DataConverter Class?The Originator
Listing
: DataConverter Class
public class DataConverter {
public static final String DATA_FILE =
Data
txt
;
public static final String OUTPUT_FILE =
SQL
txt
;
private long ID =
;
public Memento createMemento() {
return (new Memento(ID));
}
public void setMemento(Memento memento) {
if (memento != null)
ID = memento
getID();
}
public long getLastProcessedID() {
return ID;
}
public void setLastProcessedID(long lastID) {
ID = lastID;
}
public boolean process() {
boolean success = true;
String inputLine =
;
long currID =
;
try {
File inFile = new File(DATA_FILE);
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream(inFile)));
long lastID = getLastProcessedID();
while ((inputLine = br
readLine()) != null) {
StringTokenizer st =
new StringTokenizer(inputLine
);
String strID = st
nextToken();
currID = new Long(strID)
longValue();
if (lastID < currID) {
Customer c =
new Customer(strID
st
nextToken()
st
nextToken()
st
nextToken());
if (!(c
isValid())) {
success = false;
break;
}
ID = new Long(strID)
longValue();
FileUtil util = new FileUtil();
util
writeToFile(OUTPUT_FILE
c
getSQL()
true
true);
}
}
br
close();
}//Try
catch (Exception ex) {
System
out
println(
An error has occurred
+
ex
getMessage());
System
exit(
);
}
if (success == false) {
System
out
println(
An error has occurred at ID=
+
currID);
System
out
println(
Data Record=
+ inputLine);
return false;
}
return true;
}
class Memento implements java
io
Serializable {
private long lastProcessedID;
private Memento(long ID) {
lastProcessedID = ID;
}
private long getID() {
return lastProcessedID;
}
}//end of class
}//end of class
ID
實例變量ID組成了DataConverter的狀態
它代表了最後一個被成功處理的客戶紀錄的客戶ID
Memento Memento定義為DataConverter的一個內部類
Memento將它的構造函數和其他方法定義為私有
在Java中
一個類可以訪問它內部類的私有成員
DataConverter可以訪問這些方法
但是其他的對象不可以訪問
因為
當應用結束的時候
DataConverter的狀態需要被保存
Memento對象需要被序列化(serialize)到一個文件中
因此
Memento類需要實現java
io
Serializable接口
以表明自己是一個可序列化(Serializable)的類
在JAVA中
一個序列化的類必須
使用transient 關鍵字明確指出不需要序列化的屬性
實現java
io
Serializable接口
可以訪問它的第一個非序列化夫類的零參數的構造函數
process process方法讀取元數據文件
通過Customer helper類驗證客戶數據
對於每一個有效的客戶紀錄
相應的SQL插入語句被寫入到輸出文件中
當遇到無效客戶紀錄時
數據轉化過程停止
createMemento 如方法名字
這個方法負責創建Memento對象
它把DataConverter對象的當前狀態保存到一個Memento實例內
並放回它
setMemento 取出輸入的Memento對象的狀態信息
重新設置DataConverter的狀態到此狀態
DCClient (Client) 客戶DCClient(Listing
)首先初始化DataConverter
調用DataConverter實例的process方法開始數據轉化過程
如果process方法在處理原數據文件期間遇到無效的客戶數據
它會調用DataConverter實例的createMemento方法捕獲當前狀態
createMemento方法返回一個Memento對象
客戶DCClient使用MementoHandler對象負責序列化Memento實例到一個文件
Listing
: DCClient Class
public class DCClient {
public static void main(String[] args) {
MementoHandler objMementoHandler = new MementoHandler();
DataConverter objConverter = new DataConverter();
objConverter
setMemento(objMementoHandler
getMemento());
if (!(objConverter
process())) {
System
out
println(
Description: Invalid data
+
Process Stopped
);
System
out
println(
Please correct the Data and
+
Run the Application Again
);
objMementoHandler
setMemento(
objConverter
createMemento());
}
}
}
一旦數據被校正
客戶DCClient就會再次運行
客戶DCClient調用MementoHandler 上的getMemento 方法請求它保存Memento對象
MementoHandler 從文件中反序列化以前的Memento對象
並把它放回給客戶
客戶把它作為DataConverter 的set
Memento方法的參數傳遞給DataConverter
DataConverter使自己返回到保存在Memento對象中的狀態
從原來停止的地方恢復數據轉化過程
MementoHandler
The MementoHandler (Listing
) 包含了Memento 對象的一個引用
客戶DCClient把一個Memento的實例傳遞給它
Listing
: MementoHandler Class
public class MementoHandler {
public static final String ID_FILE =
ID
txt
;
private DataConverter
Memento objMemento = null;
public DataConverter
Memento getMemento() {
ObjectInputStream objStream = null;
FileUtil util = new FileUtil();
if (util
isFileExists(ID_FILE)) {
//read the object from the file
try {
objStream = new ObjectInputStream(
new FileInputStream(new File(ID_FILE)));
objMemento = (DataConverter
Memento)
objStream
readObject();
objStream
close();
} catch (Exception e) {
System
out
println(
Error Reading Memento
);
System
exit(
);
}
//delete the old memento
util
deleteFile(ID_FILE);
}
return objMemento;
}
public void setMemento(DataConverter
Memento memento) {
ObjectOutputStream objStream = null;
//write the object to the file
try {
objStream = new ObjectOutputStream(
new FileOutputStream(new File(ID_FILE)));
objStream
writeObject(memento);
objStream
close();
} catch (Exception e) {
System
out
println(
Error Writing Memento
);
System
exit(
);
}
}
}//end of class
如上面介紹的
任何時候數據轉化過程沒有驗證完全部的原數據文件
客戶要捕獲DataConverter的狀態到一個Memento中
並停止應用程序
為了使這個Memento在下次運行的時候有效
它必須被保存到持久介質上
這就涉及到了對象的序列化
如果在下次運行的時候
DataConverter要返回到它原來的狀態
這個Memento對象必須被重構
這又涉及到了對象的反序列化
這些細節由MementoHandler類來處理
使得所有客戶(DataConverter和Memento對象)免於處理這些細節
這樣也是的改變Memento的存儲方式變得很容易
例如
Memento需要保存到數據庫中
而不是文件中時
只要修改MementoHandler就可以了
不需要修改任何客戶類的實現
圖
顯示了在數據轉化這個例子中
不同對象之間的關聯關系
![](http://tw.wingwit.com/Article/UploadFiles/201311/2013112319544441.jpg)
Figure : Data Conversion Application?Class Association
Figure
shows the application message flow
![](http://tw.wingwit.com/Article/UploadFiles/201311/2013112319544414.jpg)
Figure : Application Message Flow
例子
(自己找的)
經常使用計算機的人恐怕對系統備份(Memento)不會陌生
當你的Windows系統運行正常時
對它進行備份
當系統運行有問題時
就可以調用備份快速的將系統恢復
這樣就可以大量節省重新裝系統的痛苦
特別是當你缺少某一驅動
或在裝系統是出現一些怪問題時
猶為痛苦
我想有過這種經歷的人應該很了解吧
呵呵!
好了
下面讓我們看看這個過程該如何實現吧
我們先定義Windows系統(WindowsSystem)類
public class WindowsSystem {
private String state;
public Memento createMemento() { //創建備份
保存當前狀態
return new Memento(state);
}
public void restoreMemento(Memento memento){ //從備份中恢復系統
this
state=memento
getState();
}
public String getState(){ //獲得狀態
return this
state;
}
public void setState(String state){ //設置狀態
this
state=state;
System
out
println(當前系統處於+this
state);
}
}
再定義備份(Memento)類
public class Memento {
private String state;
public Memento(String state) { //備份
this
state=state;
}
public String getState(){ //獲得狀態
return this
state;
}
public void setState(String state){ //設置狀態
this
state=state;
}
}
定義用戶(User)類
public class User {
private Memento memento;
public Memento retrieveMemento() { //恢復系統
return mento;
}
public void saveMemento(Memento memento){ //保存系統
mento=memento;
}
}
編寫測試類
public class Test {
public static void main(String args[]) {
WindowsSystem Winxp = new WindowsSystem(); //Winxp系統
User user = new User(); //某一用戶
Winxp
setState(好的狀態); //Winxp處於好的運行狀態
user
saveMemento(Winxp
createMemento()); //用戶對系統進行備份
Winxp系統要產生備份文件
Winxp
setState(壞的狀態); //Winxp處於不好的運行狀態
Winxp
restoreMemento(user
retrieveMemento()); //用戶發恢復命令
系統進行恢復
System
out
println(當前系統處於+Winxp
getState());
}
}
說明
A
定義
Memento對象是一個保存另外一個對象內部狀態拷貝的對象
這樣以後就可以將該對象恢復到原先保存的狀態
B
Memento模式的用意是在不破壞封裝的條件下
將一個對象的狀態捕捉住
並外部化
存儲起來
從而可以在將來合適的時候把這個對象還原到存儲起來的狀態
C
Memento模式所涉及的角色有三個
備忘錄角色
發起人角色和負責人角色
備忘錄角色的作用 (
) 將發起人對象的內部狀態存儲起來
備忘錄可以根據發起人對象的判斷來決定存儲多少發起人對象的內部狀態
(
) 備忘錄可以保護其內容不被發起人對象之外的任何對象所讀取
發起人角色的作用 (
) 創建一個含有當前內部狀態的備忘錄對象
(
) 使用備忘錄對象存儲其內部狀態
負責人角色的作用
(
) 負責保存備忘錄對象
(
) 不檢查備忘錄對象的內容
D
在本例中
備份(Memento)類是備忘錄角色
Windows系統(WindowsSystem)類是發起人角色
用戶(User)類是負責人角色
From:http://tw.wingwit.com/Article/program/Java/gj/201311/27656.html