首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 設計模式無廢話系列(十八):Command 命令模式

     編輯:關於C#

意圖

將請求封裝為對象,以便用不同請求參數化客戶;支持請求排隊、記錄日誌、可撤銷操作。

適用場景

網遊伺服端接收客戶端移動請求:需檢測兩次請求間隔,過短視為外掛——不執行當前請求並回滾上一次。簡化通訊層,核心問題在於如何控制方法執行時機、撤銷與組合。

若在 Man 類中硬編碼判斷邏輯,違反單一職責;且參數、回滾策略(依頻率回滾移動或裝備損耗)會讓類別膨脹。Command 模式將方法提升為類別層次,賦予對時間、撤銷、組合行為的控制力。

完整範例程式碼

using System;
using System.Collections.Generic;
using System.Text;

namespace CommandExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Man man = new Man();
            Server server = new Server();
            server.Execute(new MoveForward(man, 10));
            System.Threading.Thread.Sleep(50);
            server.Execute(new MoveRight(man, 10));
            server.Execute(new MoveBackward(man, 10));
            server.Execute(new MoveLeft(man, 10));
        }
    }

    class Man
    {
        int x = 0, y = 0;
        public void MoveLeft(int i)   => x -= i;
        public void MoveRight(int i)  => x += i;
        public void MoveForward(int i) => y += i;
        public void MoveBackward(int i) => y -= i;
        public void GetLocation() => Console.WriteLine($"({x},{y})");
    }

    abstract class GameCommand
    {
        public DateTime Time { get; set; }
        protected Man man;
        public Man Man { get => man; set => man = value; }
        protected GameCommand(Man man) { Time = DateTime.Now; this.man = man; }
        public abstract void Execute();
        public abstract void UnExecute();
    }

    class MoveLeft : GameCommand
    {
        int step;
        public MoveLeft(Man man, int i) : base(man) { step = i; }
        public override void Execute() => man.MoveLeft(step);
        public override void UnExecute() => man.MoveRight(step);
    }
    class MoveRight : GameCommand
    {
        int step;
        public MoveRight(Man man, int i) : base(man) { step = i; }
        public override void Execute() => man.MoveRight(step);
        public override void UnExecute() => man.MoveLeft(step);
    }
    class MoveForward : GameCommand
    {
        int step;
        public MoveForward(Man man, int i) : base(man) { step = i; }
        public override void Execute() => man.MoveForward(step);
        public override void UnExecute() => man.MoveBackward(step);
    }
    class MoveBackward : GameCommand
    {
        int step;
        public MoveBackward(Man man, int i) : base(man) { step = i; }
        public override void Execute() => man.MoveBackward(step);
        public override void UnExecute() => man.MoveForward(step);
    }

    class Server
    {
        GameCommand lastCommand;
        public void Execute(GameCommand cmd)
        {
            Console.WriteLine(cmd.GetType().Name);
            if (lastCommand != null && (TimeSpan)(cmd.Time - lastCommand.Time) < TimeSpan.FromMilliseconds(20))
            {
                Console.WriteLine("Invalid command");
                lastCommand.UnExecute();
                lastCommand = null;
            }
            else
            {
                cmd.Execute();
                lastCommand = cmd;
            }
            cmd.Man.GetLocation();
        }
    }
}

程式碼執行結果

Command 模式執行結果

程式碼解析

  • 僅示範防刷頻與回滾,未處理實際網路通訊。
  • Man:Receiver(接收者),執行具體操作。
  • GameCommand:抽象命令,定義統一 Execute/UnExecute 介面,記錄時間戳與持有 Receiver。
  • MoveXXX:具體命令,封裝參數並轉發 Receiver 方法;UnExecute 執行反向操作。
  • Server:Invoker(調用者),決定何時執行命令、檢查間隔、決定是否回滾上一命令。

何時採用

  • 命令發起者與接收者生命週期不同(如遺囑執行)。
  • 需賦予命令對象屬性:排隊、日誌、撤銷(GUI 常見)、宏命令組合。
  • 將方法提升為類別後,易與創建型、結構型模式組合擴展。

實作要點

  • 活動序列:客戶端指定 Receiver → 建立具體命令 → 透過 Invoker 執行 → Invoker 適時發令 → 命令呼叫 Receiver 落實。
  • 結構變化多,核心是抽象命令介面:方法類別化 + 統一執行介面。
  • Invoker 可延遲執行、記錄日誌、撤銷命令、組合宏命令(配合 Composite)。
  • 可省略非關鍵角色:單一 Receiver 或邏輯簡單可合併至具體命令;無額外 Invoker 功能可省略 Invoker。
  • Undo 通常要求 Receiver 也提供反向介面;實務中 Undo 往往涉及複雜環境還原,非單純呼叫方法。

注意事項

  • 別被複雜結構迷惑:「將方法提升至類別層次的好處,即 Command 模式的好處」。
  • 相比 State/Strategy 將狀態/算法提升為類別,Command 可能產生更多類別/對象。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved