C#中使用單個對象的方法實現Undo/Redo

2021-01-07 IT168

  【IT168 專稿】

  簡介

  我們如何在不同的場景下使用這些方法來實現Undo/Redo。這些方法是使用單個對象表示變化,命令模式和備忘錄模式。
 
  正如我們所知,Undo/Redo沒有通用的解決方案,而Undo/Redo在每個應用程式中非常具體。處於這個原因,在該系列文章的開始部分,將討論如何使用該方法建模任意的應用程式,然後展示一個簡單應用程式的實現。

  關於Undo/Redo實現的基本思想

  正如我們所知,應用程式在每次操作後改變其狀態。當操作應用程式時,它的狀態會發生改變。所以,若有人想要做撤銷,他不得不回到先前的狀態。因此,為了能夠回到先前狀態,我們需要在應用程式運行時存儲它的狀態。要支持重做,我們不得不從目前狀態跳到下一個狀態。

  為了實現Undo/Redo,我們不得不存儲應用程式的狀態並在撤銷時跳到前一個狀態而在重做時跳到下一個狀態。因此我們需要維護應用程式的狀態來支持Undo/Redo。在所有三種方法中,應用程式狀態的維護用到了兩個棧。一個棧包含用於撤銷操作的狀態,第二個包含用於重做的狀態。撤銷操作彈出撤銷棧以獲取前一個狀態並將其設置給應用程式。同樣的,重做操作彈出重做棧以獲取下一個狀態並將其設置給應用程式。

  現在,我們知道了Undo/Redo的實現操作都是關於保持應用程式每次操作後的狀態。現在的問題是該方法如何保存狀態。本方法中,單個操作的改變被保存在一個對象中,有些屬性為該操作作為狀態是多餘的,因為這裡,單個對象被用於包含所有類型的動作數據。

  什麼是單個對象表示改變的方法?

  首先,我對這是由我命名表示抱歉。這裡,單個對象表示了應用程式中所有操作的所有改變。因此,當你準備了一個關於操作更改的類型的對象,在執行一次操作後,你僅使用了該對象屬性的子集,而剩餘屬性仍舊沒有被使用。例如,你在一個應用程式中有兩個操作;它們是高度的改變和寬度的改變。因此,對象類型包含兩個屬性:高度和寬度。當你準備了變化對象,在執行高度更改方法後,你僅需設置變化對象的高度欄位,而其他欄位仍舊沒有被使用。

  如何應用單個對象表示變化的方法對任意應用程式Undo/Redo操作建模?

  單個對象表示變化的方法如何對任意應用程式Undo/Redo操作建模將在以下步驟中討論:

  步驟1

  首先識別出你希望哪些操作能支持Undo/Redo。然後,識別出你將在哪個容器下支持Undo/Redo以及你希望哪些對象支持Undo/Redo。

  步驟2

  為了進一步處理每個Undo/Redo操作,識別出需要被保存的屬性。

  步驟3

  然後創建一個類(ChangeRepresentationObject),它包含支持全部操作Undo/Redo的所有屬性。同樣,準備一個動作類型enum,它將代表全部操作。這個動作類型enum是ChangeRepresentationObject類的一部分。

  步驟4

  然後創建一個名為UndoRedo的類,它包含兩個類型的ChangeRepresentationObject棧。一個用於撤銷操作,一個用於重做操作。該類將實現以下接口:

  interface IUndoRedo
    {
        void Undo(int level);
        void Redo(int level);
        void InsertObjectforUndoRedo(ChangeRepresentationObject dataobject);
    }

  步驟5

  然後實現具體方法:Undo、 Redo、InsertObjectforUndoRedo。
在每個Undo操作中:

  •首先檢查Undo棧是否為空。
  •如果不是,則彈出一個ChangeRepresentationObject並將其壓入重做棧。
  •檢查動作類型。
  •然後基於動作類型,利用ChangeRepresentationObject的屬性完成撤銷操作。
  在每個Redo操作中,你幾乎做與Undo同樣的事。
  •首先檢查Redo棧是否為空。
  •如果不是,彈出一個ChangeRepresentationObject,然後將其壓入撤銷棧。
  •檢查動作類型。
  •然後基於動作的類型,利用ChangeRepresentationObject屬性完成重做操作。
  在InsertObjectforUndoRedo操作中,你只要把數據對象插入Undo棧並清空Redo棧中。

  步驟6

  然後,在完成每次操作前,調用InsertObjectforUndoRedo方法以對所有操作提供Undo/Redo支持。在用戶界面上點擊Undo時,只需調用UndoRedo類的Undo方法,而在用戶界面上點擊Redo時,只需調用UndoRedo類的redo方法。

相關焦點

  • C#中使用命令模式實現Undo/Redo
    第一篇:C#中使用單個對象的方法實現Undo/Redo  為了實現Undo/Redo,我們不得不存儲應用程式的狀態並在撤銷時跳到前一個狀態而在重做時跳到下一個狀態。因此我們需要維護應用程式的狀態來支持Undo/Redo。在所有三種方法中,應用程式狀態的維護用到了兩個棧。一個棧包含用於撤銷操作的狀態,第二個包含用於重做的狀態。
  • 詳解MySQL的Redo日誌與Undo日誌
    下面通過3個redo日誌來討論:(a) 在日誌中只有部分記錄,可能事務在執行時系統發生了崩潰,這時需要根據日誌重做(以下統一使用術語redo)。1.3 UndoUndo是邏輯日誌,並不冪等,在撤銷時,根據undo記錄進行補償操作。Undo本身也產生redo記錄。通過undo日誌資料庫可以實現MVCC。Undo保證了事務失敗或主動abort時的機能,除此之外,系統崩潰恢復時,也確保資料庫狀態能恢復到一致。系統恢復時,undo需要redo的配合來實現,或者說二者是一套機制的兩個方面。
  • mysql分布式事務,undo和redo,java學習這塊必須要懂?
    因為在傳統項目中,項目部署基本是單點式:即單個伺服器和單個資料庫。這種情況下,資料庫本身的事務機制就能保證ACID的原則,這樣的事務就是本地事務。概括來講,單個服務與單個資料庫的架構中,產生的事務都是本地事務。其中原子性和持久性就要靠undo和redo 日誌來實現。
  • MySQL 日誌(redo log 和 undo log) 都是什麼鬼?
    並且二進位日誌先於redo log被記錄。具體的見後文group commit小結。二進位日誌記錄操作的方法是邏輯性的語句。即便它是基於行格式的記錄方式,其本質也還是邏輯的SQL設置,如該行記錄的每列的值是多少。而redo log是在物理格式上的日誌,它記錄的是資料庫中每個頁的修改。
  • 必須了解的mysql三大日誌-binlog、redo log和undo log
    作為開發,我們重點需要關注的是二進位日誌( binlog )和事務日誌(包括redo log 和 undo log ),本文接下來會詳細介紹這三種日誌。binlogbinlog 用於記錄資料庫執行的寫入性操作(不包括查詢)信息,以二進位的形式保存在磁碟中。
  • 必須了解的MySQL三大日誌:binlog、redo log和undo log
    作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。binlogbinlog用於記錄資料庫執行的寫入性操作(不包括查詢)信息,以二進位的形式保存在磁碟中。
  • InnoDB undo log
    >使用多個鍊表實現維護trx_undo_t信息所有回滾段都存在trx_sys->rseg_array,數組大小為128,分別對應不同的回滾段,rseg_array類型為trx_rseg_t用於維護回滾段相關的信息trx_rseg_ttrx_undo_t事務開啟時,會分配指定回滾段,以後該事務用到的undo log頁,就從該回滾段上分配
  • 【140期】MySQL中的redolog,undolog,以及binlog的區別及各自作用是什麼?
    什麼時候產生:事務開始之後就產生redo log,redo log的落盤並不是隨著事務的提交才寫入的,而是在事務的執行過程中,便開始寫入redo log文件中。,而不是從物理頁面上操作實現的,這一點是不同於redo log的。
  • ...實戰備忘錄模式「模擬網際網路系統上線過程中,配置文件回滾場景」
    在功能實現上是以不破壞原對象為基礎增加備忘錄操作類,記錄原對象的行為從而實現備忘錄模式。這個設計在我們平常的生活或者開發中也是比較常見的,比如:後悔藥、孟婆湯(一下回滾到0),IDEA編輯和撤銷、小霸王遊戲機存檔。
  • Seata 實現分布式事務原理,mysql日誌記錄問題,學習下
    因為redo log已經持久化,因此資料庫數據寫入磁碟與否影響不大,不過為了避免出現髒數據(內存中與磁碟不一致),事務提交後也會將內存數據刷入磁碟(也可以按照固設定的頻率刷新內存數據到磁碟中)。redo log何時寫入磁碟redo log會在事務提交之前,或者redo log buffer滿了的時候寫入磁碟這裡存在兩個問題:問題1:之前是寫undo和資料庫數據到硬碟,現在是寫undo
  • C#代碼C#代碼高級寫法—Linq表達式Select方法多使用場景
    有時我們只需要list中的單個屬性,例如是需要查詢井的信息,那麼我們只需要知道井ID的list集合。那麼我們就可以使用select語句投影成到這個井ID屬性。需求:篩選出組織機構下的單井,拿到井ID屬性做法:先使用where子句篩選是否為組織機構的單井,通過select方法投影出井的IDvar allOilWellsId = wellNode .Where(x => x is WellOrganDescriptor) .OfType<WellOrganDescriptor
  • C#和.NET向JAVA好轉嗎?
    1Java和C#都是完全面向對象的語言在面向對象編程的三大原則方面,這兩種語言接近得不能再接近。不過也有一些差別,不過不多,稍微習慣下就好了,比如:集合:兩種語言都有集合ArrayList,還有通過鍵訪問值的Java中是HashMap而c#中是HashTable。c#比Java多泛型集合List<T>與Dictionary<K,V>更容易了,無需拆箱裝箱了,更安全了。
  • [譯]聊聊C#中的泛型的使用
    >通用方法雖然大多數開發人員通常會使用基類庫中的現有泛型類型,但也有可能會構建自己的泛型成員和自定義的泛型類型。本示例的目的是構建一個交換方法,該方法可以使用單個類型參數對任何可能的數據類型(基於值或基於引用)進行操作。由於交換算法的性質,傳入的參數將作為使用ref關鍵字修飾的引用類型來進行發送。
  • Python實現的簡易筆記本
    主要的要點有如下幾點 :① 使用wxPython進行GUI的開發 ;② 使用win32ui這個Python模塊進行文件的打開與另存為GUI的編寫(關於這個知識點,網上搜索或者使用Python的方法即可學會);③ wx.TextCtrl這個控制項自帶複製、粘貼、剪切、撤銷、全選等功能 。
  • 一文搞懂undo log版本鏈與ReadView機制如何讓事務讀取到該讀的數據
    對於每行有兩個隱藏的欄位,在《高性能 MySQL》第三版的第 13 頁中把它們叫做數據的更新時間和過期時間,這兩個欄位存儲的不是真實的時間,而是事務的版本號。這與本文 row_trx_id 和 roll_pointer 的叫法差異很大,實際上,不用在意這兩個欄位具體叫什麼,反正它們都是為了實現 MVCC 機制而設計的。
  • 零基礎學Oracle之9:Oracle redo log file實驗
    注意:不能刪除組中的最後一個成員;處於current或active狀態時的日誌文件組不能刪除其中的成員;在archive模式下,未歸檔的log file不呢個刪除其組成員;刪除聯機重做日誌文件成員時,如果沒有使用OMF 功能,則不會刪除作業系統文件。
  • 中文語音識別技術在c#中的應用(二)
    正在閱讀:中文語音識別技術在c#中的應用(二)中文語音識別技術在c#中的應用(二)2004-06-11 10:04出處:CSDN作者:tashanzhishi>  接上篇《中文語音識別技術在c#中的應用(一)》………但是,這個方法本身並不知道你給的字符串是什麼語言,所以需要我們它這個字符串用什麼語言讀出。