輕鬆實現記錄與撤銷——C#中的Command模式

来源:https://www.cnblogs.com/deatharthas/archive/2020/05/31/13021590.html
-Advertisement-
Play Games

Command模式屬於行為模式,作為大名鼎鼎的23個設計模式之一,Command模式理解起來不如工廠模式,單例模式等那麼簡單直白。究其原因,行為模式著重於使用,如果沒有編程實踐,確實不如創造模式那麼直白。我們先看看UML類圖。 估計很多同學看著圖就暈了,那麼多東西,Command和Concrete ...


Command模式屬於行為模式,作為大名鼎鼎的23個設計模式之一,Command模式理解起來不如工廠模式,單例模式等那麼簡單直白。究其原因,行為模式著重於使用,如果沒有編程實踐,確實不如創造模式那麼直白。我們先看看UML類圖。

估計很多同學看著圖就暈了,那麼多東西,Command和Concrete Command還好理解,那些Receiver和Invoker又是什麼東西呢?
 
彆著急,只要理解了一點,這個模式就很容易理解了,下麵劃重點,Command模式最主要的特點,是將命令封裝成類,在類中保存命令執行的上下文(即該命令執行的參數,執行的對象),以實現命令執行對象和命令發出對象的解耦
 
這樣一來是不是覺得好理解多了?Command類裡面的Receiver,就是命令具體執行的對象。這裡的Client可以理解為裝配環境,在這裡面代碼實例化Command。Invoker內部保存命令(可以保存多條命令,實現命令記錄查看,撤銷等),客戶端代碼通過Invoker來操作命令。接下來我們看看示例代碼。

 

定義Command介面

首先我們定義一個支持撤銷的Command介面。

    interface Command
    {
        void Execute();
        void Undo();
    }

定義Receiver

接下來我們定義Receiver,也就是命令的執行對象,這裡我們定義一個Ball類。

    class Ball
    {
        public int Size { get; set; } = 10;
        public string Name { get; set; } = "My First Ball";
        public void Inspect()
        {
            Console.WriteLine("My Name is {0} and size is {1}", Name, Size);
        }
    }

定義具體命令

這裡定義兩個命令,一個修改名字,一個修改大小。

    class ChangeNameCommand : Command
    {
        private Ball _Ball;
        private string _OldName;
        public string NameYouWant { get; set; }
        public ChangeNameCommand(Ball ball)
        {
            _Ball = ball;
        }

        public void Execute()
        {
            _OldName = _Ball.Name;
            _Ball.Name = NameYouWant;
        }

        public void Undo()
        {
            _Ball.Name = _OldName;
        }
    }
    
	class ChangeSizeCommand : Command
	{
	//代碼大同小異,略
	}

定義Invoker

接下來是Invoker,,也就是存儲命令,並最終會被用戶代碼調用的類,這裡我們叫它CommandManager。

    class CommandManager
    {
        private Stack<Command> commands = new Stack<Command>();

        public void RunCommand(Command command)
        {
            command.Execute();
            commands.Push(command);
        }

        public void Undo()
        {
            if (commands.Count > 0)
            {
                var command = commands.Pop();
                command.Undo();
            }
        }
        
		public void ShowCommands()
        {
            var temp = commands.Reverse();
            foreach(var command in temp)
            {
                //display command
            }
        }
    }

使用命令

現在我們看看客戶端代碼是怎麼使用他們的,定義Ball,定義命令,通過CommandManager去調用,這樣可以方便查看命令記錄,撤銷命令,等。

        static void Main(string[] args)
        {
            Ball ball = new Ball();
            ball.Inspect();

            ChangeNameCommand changeName = new ChangeNameCommand(ball) { NameYouWant = "Changed" };
            ChangeSizeCommand changeSize = new ChangeSizeCommand(ball) { SizeYouWant = 20 };

            CommandManager manager = new CommandManager();
            manager.RunCommand(changeName);
            manager.RunCommand(changeSize);
            ball.Inspect();

			manager.ShowCommands();
            
            manager.Undo();
            ball.Inspect();

            manager.Undo();
            ball.Inspect();            
        }

就醬,我們已經實現了命令模式,並且還支持命令的記錄與撤銷,希望能對大家有點幫助。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Vue 打包後自定義樣式無法覆蓋elementUI組件原有樣式問題 by:授客 QQ:1033553122 開發環境 Win 10 node-v10.15.3-x64.msi 下載地址: https://nodejs.org/en/ 問題描述 如下為基於elementUI Dialog編寫的一個組件 ...
  • 上午下午晚上分別顯示不同的問候語 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> ...
  • 一、前言 在vue的視圖層與modal層進行數據交互的時,視圖層的數據傳入到modal層,modal層通過defineProperty來劫持每個元素,並綁定監聽事件進行監聽,一旦監聽到數據變化,就通過defineProperty的set函數重新更新視圖層。 二、使用Object.defineProp ...
  • 一、當用戶在瀏覽器地址欄中輸入網址,到看到頁面,經歷的步驟 tips:hexo自己搭一個博客 1.解析輸入的URL地址 傳輸協議(把信息在客戶端和伺服器端進行傳遞,類似於快遞小哥) http 超文本傳輸協議(傳輸的內容除了文本,還有可能是其它類型:二進位編碼、BASE64碼、文件流等等) https ...
  • 不管你是做哪個語言的,只要是個程式員都懂: 哈哈啥哈哈哈 來跟我一起學習java吧,做個牛b的程式員。我們一起來敲bug吧 “大清亡於閉關鎖國,學習技術需要交流和資料”。 在這裡我給大家準備了很多的學習資料免費獲取,包括但不限於技術乾貨、大廠面試題系列、技術動向、職業生涯等一切有關程式員的分享. w ...
  • # 5.Utilities:工具 - 1. 邊框 中文網站邊框部分:http://bs4.vx.link/index.html?tmpui_page=/pages/utilities - 2. 清除浮動 - 3. 關閉圖標 - 4. 顏色 - 5. 顯示 3.x版本顯示只有三種:block, inl ...
  • 一、 1、打開方式 打開Chrome瀏覽器,按下F12或者右擊空白處然後點擊檢查 最左邊是顯示效果,中間是html代碼,右邊是html樣式。 2、樣式的修改 點擊中間代碼框,左上角的小箭頭,然後點擊css樣式,可以直接修改屬性的值。也可以點擊鍵盤上的上下箭頭,對屬性的值進行修改 需要註意的是,調試工 ...
  • 平時常用的一些功能性函數 關於原生JS 文件大小單位轉換 /** * @desc bytesToSize 位元組單位換算 * @param bytes 傳入以bit為單位的數據 */ const bytesToSize = function (bytes) { const k = 1024; if ( ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...