備忘錄模式其實就是給我們的應用程式一次撤銷的機會。使用過word的人肯定會知道偉大的「ctrl+z」,用過ps的人更是不會忘記,應該來說基本上所有的帶編輯功能的軟體毫無例外都提供了撤銷的功能,撤銷功能給了我們1次或n次返回的機會,準確地說應該是恢復之前狀態的機會。我們自己開發的軟體有時候有需要撤銷的功能,比如在網路通訊中,常常會因為不可預知的錯誤就導致程式出錯,這時候,要是能恢復到上乙個正確的狀態就太好了,這樣可以省去不上功夫。我們今天要討論的備忘錄模式就是用來解決這個問題的。
經典的備忘錄模式如下圖所示:
注:本圖來自《設計模式_基於c#的工程化實現及擴充套件》
首先,還是讓我來解釋一下這幅圖的意思:originator(原發器)就是我們的業務模型,它本身可能有非常多的字段(或叫做變數)。乙個欄位的值發生了改變,我們可以認為originator的狀態就發生了變化。originator裡面有乙個imemento型別的字段用來記錄狀態的變化(它相當於乙個記事本,用來備忘的)。imemento聚合與caretaker,caretaker也就是負責人,用來決定什麼時候將imemento(備忘錄)物件中的狀態還原到originator(原發器)中。打個形象的比喻,originator就好比乙個圖書館,圖書館裡面有很多書,每一本書都有接還狀態(後面我們會提到istate用來表示狀態),imemento就想到於圖書館管理員手中的接還本(或稱之為備忘錄),caretaker就相當於圖書館管理員,圖書館管理員(caretaker)儲存有一本(或n本)備忘錄。當一本書(假設是booka)借出去的時候,圖書館(originator)的booka的狀態(istate)就發生了變化,變成借出狀態。等到還書的時候,圖書館管理員(caretaker)再通過備忘錄(imemeto)物件將圖書館(originator)還原成原始的狀態。需要說明的是,這個比喻並不恰當,因為書的借出、歸還就兩種狀態,但是實際應用中,因為originator中的變數的取值範圍的大小,originator中的某乙個變數可以有很多狀態,這時候才是備忘錄發揮作用的時候。
1、備忘錄模式裡面涉及到的型別有caretaker(負責人型別)、originator(原發器型別)、imemento(備忘錄型別)、istate(狀態型別),在下面的**中,我們將不會涉及到實際的caretaker型別。我們先定義狀態型別(istate),我們下面只是單純地定義乙個介面,實際開發過程中可以為istate介面定義一些方法、屬性。
///2、定義原發器、備忘錄抽象基類///狀態型別
/// public
inte***ce
istate
//////
備忘錄
/// ///
狀態型別
public
inte***ce imementowhere
t : istate
}//////
原發器
/// ///
狀態型別
///備忘錄型別
public
inte***ce ioriginatorwhere
t : istate
where m : imemento,new()//
以便後面取出對原發器進行還原
}
//////備忘錄基類
/// ///
狀態型別
public
abstract
class mementobase:imementowhere
t:istate
set }
}
///3、我們在定義業務邏輯中使用的備忘錄和原發器,有了前面的基礎,現在我們就好辦了。我們將要實現定定游標位置的軟體,在必要的時候,我們要將現在的座標恢復到上乙個游標的位置。我們將定義乙個position型別來儲存游標的x、y軸數值(也就是狀態,所以position實現istate),用乙個實體型別的originator(原發器)實現對游標的操作。///原發器基類
/// ///
狀態型別
///備忘錄型別
public
abstract
class originatorbase: ioriginatorwhere
t : istate
where m : imemento,new
()
set }
}
//////座標
/// public
class
position : istate
public
int y
}
//////備忘錄實體類
/// public
class memento:mementobase{}
///4、定義完了介面和抽象類,現在我們來看看如何實際地應用備忘錄模式///原發器實體類
/// public
class originator:originatorbase//
共客戶程式使用的非備忘錄相關操作
public
void decrea***()
public
void increasey()
public position current }
}
[testmethod()]在上面**中,我們並沒有引入caretaker,而是直接在**中指定還原點(見紅色注釋),實際應用中可能要再建立乙個caretaker類,以便在系統出現異常或者在某種特定的情況下對狀態進行還原。public
void
test()
有同學會說,為了備份乙個類裡面的一些狀態,有必要搞得這麼複雜嗎?還定義了istate、ioriginator、imemento三種介面,更恐怖的是還有對應的抽象基類和實體類。其實,這的確增加了需要**的複雜程度,如果需要備份的狀態幾乎不會發生變化,那麼其實簡單地在業務邏輯物件(originator)裡面增加一些欄位或乙個類簡單地記錄一下就行。但是如果原發器中有不同的狀態,我們有時候只需要記錄其中乙個變數的狀態或幾個變數的狀態,或者要記錄的狀態有時候會因為業務規則而發生改變,我們又不想讓這個改變影響太多的**,那麼如同以上**,我們將狀態和備忘錄抽象為isate和imemento,這樣就可以非常方便地對會變動的狀態進行調整。
kissazi2
備忘錄模式
備忘錄模式 memento 在不破壞封裝性的前提下,捕獲乙個物件的內部狀態,並在該物件之外儲存這個狀態。這樣以後就可將該物件恢復到原先儲存的狀態。originator 發起人 負責建立乙個備忘錄memento,用以記錄當前時刻它的內部狀態,並可以使用備忘錄恢復內部狀態。originator可根據需要...
備忘錄模式
先從物件導向的三大特徵之一封裝說起。物件導向的封裝簡單點說就是把狀態 資料 和行為 操作這些資料的方法 放到一起,構成乙個單元,通常叫做類。乙個物件的行為是事先確定好的 靜態 一些指令碼,如果物件的狀態相同,物件看起來就是一樣的。所以當我們需要把乙個物件的某一時刻儲存起來,那麼只需要儲存它在那個時刻...
備忘錄模式
面臨問題 物件狀態的變化無端,如何回溯恢復物件在某個點的狀態?在軟體構建過程中,某些物件的狀態在轉換過程中,可能由於某種需要,要求程式能夠回溯到物件之前處於某個點時的狀態。如果使用一些公用介面來讓其他物件得到物件的狀態,便會暴露物件的細節實現。如何實現物件狀態的良好儲存與恢復?但同時又不會因此而破壞...