一、基於對象序列化的Undo\Redo
在Rockford Lhotka的CSLA框架中,介紹了一種基於保存序列化對象入棧的Undo\Redo實現方案。調用BeginEdit函數時,通過反射機制將整個業務對象的所有Field序列化,並保存在UndoStack中。在Undo時,將保存在UndoStack中的序列化的Map值讀出來,對現有的業務對象進行數值還原。
由於使用對象序列化的方式來保存對象的歷史。所以當UndoStack比較深,或是業務對象比較大的時候會佔用比較多的內存,性能上也不盡如人意。但是CSLA的Undo實現方式通用性較好。所以適用範圍還是不小的。
二、基於Command模式的Undo\Redo
GoF的名著《Design Patterns》中介紹了著名的Command的模式,主要用途之一就是用來實現Undo\Redo的。具體實現方式就不在這裏介紹了,有興趣可以參考《Design Patterns》一書。
靈活運用這種實現方式可以應對各種Undo操作,但是是以損失一定的可複用性爲代價的。一個可以Undo的操作越具體,它和實現應用的關係就越緊,抽象性就越差,也就越難以複用。其極端情況就是每個Command都要符合ACID。
最重要的是,如果要使用Command模式來實現Undo機制,最好是在開始寫代碼之前就做這個決定,並自始至終使用Command來響應用戶請求。如果等到所以的代碼都寫到Click事件處理函數裏的時候,再想用Command來實現Undo\Redo就麻煩了。
三、結合RoutedEvent、Observer模式和Command模式的Undo\Redo
爲了解決上面兩種Undo實現方式的缺點,既保證通用性,又能減少資源佔用。可以將上面的兩種方式結合起來,並聯合和RoutedEvent機制和Observer模式。
RoutedEvent是.NET 3.0中的WPF裏的新型的事件傳遞機制。主要特點是子對象的事件發生後會觸發父對象的相應事件。從而簡化事件的訂閱。但是WPF裏的RoutedEvent只能用在UIElement上,業務對象就無福消受了。所以可以自己實現一個簡單的RoutedEvent機制來簡化事件的訂閱。
然後用一個Watcher對象監視業務對象的變化,將變化抽象、封裝成一個可以Undo、Redo的Command。大致可以分成PropertyChangedEdition和ItemChangedEdition兩種,就可以提供對於屬性改變、從List中刪除、向List添加、List中Item Move這些常見操作的Undo、Redo了。
當然這種方式也有不好的地方,由於抽象程度比較高,所以這種Undo機制並不能很好地和業務邏輯契合,比如List A和List B的添加、刪除是同步且應該被視爲一個操作時,這種機制還是認爲是兩個操作。
上面介紹了兩種常見的Undo、Redo的方式和一種新的實現方式,算是拋磚引玉,幫助大家找到更適合自己的Undo Redo實現方式。
例子:http://www.cnblogs.com/vivid-stanley/archive/2007/02/13/649824.html
Qt Undo Framework : http://www.cppblog.com/eryar/archive/2015/01/13/209504.html QUndoCommand