C#面向對象設計模式縱橫談 第21講:Memento 備忘錄模式

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統

2006.8.29 李建忠

對象狀態的回溯

對象狀態的變化無端,如何回溯/恢復對象在某個點的狀態

image

如果我們想恢復對象的狀態,那么我們可能首先想到的是把對象保存下來,但是這樣會破壞對象的封裝性。因為對象有狀態有操作,如果我們為了保存對象而留著原來的對象,做一個深拷貝,那么其他對象也能通過這個對象的接口訪問這個對象狀態,這并不是我們所希望的。而我們需要它的職責只是保存和恢復對象狀態,而不應在上面支持對對象狀態訪問的接口,這就產生了Memento模式。

 

動機(Motivation)

在軟件構建過程中,某些對象的狀態在轉換的過程中,可能由于某種需要,要求程序能夠回溯到對象之前處于某個點時的狀態。如果使用一些公有接口來讓其他對象得到對象的狀態,便會暴露對象的細節實現。

如何實現對象狀態的良好保存與恢復?但同時又不會因此而破壞對象本身的封裝性。

 

意圖(Intent)

在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態(如果沒有這個關鍵點,其實深拷貝就可以解決問題)。這樣以后就可以將該對象恢復到原先保存的狀態。

——《設計模式》GoF

 

例說Memento應用

image

image

image

備忘錄模式適用于對象狀態有頻繁的改變和恢復的需求。

image

image

這種設計方式可以解決一部分問題,但是它用的一個rSaved對象除了擁有Rectangle對象的狀態之外,還擁有Rectangle對象的操作。從對象的責任來講,拿著rSaved字段就可以更改rSaved,但是rSave對象的作用只是用來保存對象的狀態,我們不應該允許更改rSaved的內容。即使我們保證自己不改rSaved的內容,但是純OO的理念要求,只要對象上提供了操作,那么它的含義就表示做這樣的操作是沒有問題的,這也是單一職責原則。

改進后的代碼

image

image

Rectangle類不需要Clone操作,我們新增一個RectangleMomento類。

image

我們在外部程序中,拿到RectangleMomento類型的rSaved實例,什么都不能做,因為它都是internal的方法。所以這種方式使得我們既內部保存了狀態,外部又不提供操作。

image

image

其實這個和克隆很相似,只不過是克隆出來的對象可以擁有對象的接口,而Memento沒有。Memento只封裝狀態,而不再提供其它操作。

 

結構(Structure)

image

state是需要存儲的狀態字段,CreateMemento負責存儲狀態,SetMemento負責恢復狀態。

 

Memento模式的幾個要點

備忘錄(Memento)存儲原發器(Originator)對象的內部狀態,在需要時恢復原發器狀態。Memento模式適用于“由原發器管理,卻又必須存儲在原發器之外的信息”。

在實現Memento模式中,要防止原發器以外的對象訪問備忘錄對象。備忘錄對象有兩個接口,一個為原發器使用的寬接口;一個為其他對象使用的窄接口。

在實現Memento模式時,要考慮拷貝對象狀態的效率問題,如果對象開銷比較大,可以采用某種增量式改變(即只記住改變的狀態)來改進Memento模式。

 

我們也可以用序列化的方式實現備忘錄。序列化之后,我們可以把它臨時性保存到數據庫、文件、進程內、進程外等地方。ASP.Net的Session其實就有這種影子。
image

image

image

類需要加上可序列化標記。MemoryStream是一個內存流,可以對對象進行序列化和反序列化,內存流是保存到當前進程里面。操作的時候,對對象做序列化;保存的時候,對對象做反序列化。內存流的好處是操作方便,避免了我們寫很多字段來保存。

 

Memento模式與Command模式的異同

雖然兩者都支持Undo操作,但是Command是對行為的封裝,Memento是對對象狀態的保留,這是目的上的不同。它們支持的也是Undo操作的不同層面,Command是對行為序列的操作,Memento是對行為狀態的操作

2010.10.23


MSDN 網絡廣播 李建忠 2013-08-22 08:52:40

[新一篇] C#面向對象設計模式縱橫談 第20講:Chain Of Responsibility 職責鏈模式

[舊一篇] C#面向對象設計模式縱橫談 第22講:State 狀態模式
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表