1. 程式人生 > >設計模式(七) 命令模式

設計模式(七) 命令模式

receive 系統 適用場景 命令 and 完成 模式 4.4.2 cli

1. 概述

  將一個請求封裝為一個對象(即我們創建的Command對象),從而使你可用不同的請求對客戶進行參數化; 對請求排隊或記錄請求日誌,以及支持可撤銷的操作。

2. 解決的問題

  在軟件系統中,行為請求者與行為實現者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種無法抵禦變化的緊耦合的設計就不太合適。

3. 模式中角色

  3.1 抽象命令(Command):定義命令的接口,聲明執行的方法。

  3.2 具體命令(ConcreteCommand):具體命令,實現要執行的方法,它通常是“虛”的實現;通常會有接收者,並調用接收者的功能來完成命令要執行的操作。

  3.3 接收者(Receiver):真正執行命令的對象。任何類都可能成為一個接收者,只要能實現命令要求實現的相應功能。

  3.4 調用者(Invoker):要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令對象的入口。

  3.5 客戶端(Client):命令由客戶端來創建,並設置命令的接收者。

4. 模式解讀

4.1 命令模式的類圖

  技術分享圖片

4.2 命令模式的實現代碼:

技術分享圖片
/// <summary>
    /// 接收者類,知道如何實施與執行一個請求相關的操作,任何類都可能作為一個接收者。
    
/// </summary> public class Receiver { /// <summary> /// 真正的命令實現 /// </summary> public void Action() { Console.WriteLine("Execute request!"); } } /// <summary> /// 抽象命令類,用來聲明執行操作的接口 /// </summary> public interface
ICommand { void Execute(); } /// <summary> /// 具體命令類,實現具體命令。 /// </summary> public class ConcereteCommand : ICommand { // 具體命令類包含有一個接收者,將這個接收者對象綁定於一個動作 private Receiver receiver; public ConcereteCommand(Receiver receiver) { this.receiver = receiver; } /// <summary> /// 說這個實現是“虛”的,因為它是通過調用接收者相應的操作來實現Execute的 /// </summary> public void Execute() { receiver.Action(); } } /// <summary> /// 調度類,要求該命令執行這個請求 /// </summary> public class Invoker { private ICommand command; /// <summary> /// 設置命令 /// </summary> /// <param name="command"></param> public void SetCommand(ICommand command) { this.command = command; } /// <summary> /// 執行命令 /// </summary> public void ExecuteCommand() { command.Execute(); } }
View Code

4.3 客戶端代碼

    class Program
    {
        static void Main(string[] args)
        {
            Receiver receiver = new Receiver();
            ICommand command = new ConcereteCommand(receiver);
            Invoker invoker = new Invoker();

            invoker.SetCommand(command);
            invoker.ExecuteCommand();

            Console.Read();
        }
    }

  執行結果

  技術分享圖片

  4.4 模式分析

    4.4.1 本質:對命令進行封裝,將發出命令與執行命令的責任分開。

    4.4.2 每一個命令都是一個操作:請求的一方發出請求,要求執行一個操作;接收的一方收到請求,並執行操作。

    4.4.3 請求方和接收方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎麽被接收,以及操作是否被執行、何時被執行,以及是怎麽被執行的。

    4.4.4 使請求本身成為一個對象,這個對象和其它對象一樣可以被存儲和傳遞。

    4.4.5 命令模式的關鍵在於引入了抽象命令接口,且發送者針對抽象命令接口編程,只有實現了抽象命令接口的具體命令才能與接收者相關聯。 

5. 模式總結

  5.1 優點

    5.1.1 解除了請求者與實現者之間的耦合,降低了系統的耦合度。

    5.1.2 對請求排隊或記錄請求日誌,支持撤銷操作。

    5.1.3 可以容易地設計一個組合命令。

    5.1.4 新命令可以容易地加入到系統中。

  5.2 缺點

    5.2.1 因為針對每一個命令都需要設計一個具體命令類,使用命令模式可能會導致系統有過多的具體命令類。

  5.3 適用場景

    5.3.1 當需要對行為進行“記錄、撤銷/重做”等處理時。

    5.3.2 系統需要將請求者和接收者解耦,使得調用者和接收者不直接交互。

    5.3.3 系統需要在不同時間指定請求、請求排隊和執行請求。

    5.3.4 系統需要將一組操作組合在一起,即支持宏命令。

6. 應用舉例:銀行帳號的存款、提款

  6.1 類圖

技術分享圖片

代碼實現:

技術分享圖片
/// <summary>
    /// 銀行帳號
    /// </summary>
    public class Account
    {
        /// <summary>
        /// 帳號總金額
        /// </summary>
        private decimal totalAmount { get; set; }

        /// <summary>
        /// 存錢
        /// </summary>
        /// <param name="amount"></param>
        public void MoneyIn(decimal amount)
        {
            this.totalAmount += amount;
        }

        /// <summary>
        /// 取錢
        /// </summary>
        /// <param name="amount"></param>
        public void MoneyOut(decimal amount)
        {
            this.totalAmount -= amount;
        }

        public decimal GetTotalAmout()
        {
            return totalAmount;
        }
    }

    public abstract class Command
    {
        protected Account account;

        public Command(Account account)
        {
            this.account = account;
        }

        public abstract void Execute();
    }

    /// <summary>
    /// 存款命令
    /// </summary>
    public class MoneyInCommand : Command
    {
        private decimal amount;

        public MoneyInCommand(Account account, decimal amount)
            : base(account)
        {
            this.amount = amount;
        }

        /// <summary>
        /// 實現存錢命令
        /// </summary>
        public override void Execute()
        {
            account.MoneyIn(amount);
        }
    }

    /// <summary>
    /// 取款命令類
    /// </summary>
    public class MoneyOutCommand : Command
    {
        private decimal amount;
        public MoneyOutCommand(Account account, decimal amount)
            : base(account)
        {
            this.amount = amount;
        }

        /// <summary>
        /// 實現取錢命令
        /// </summary>
        public override void Execute()
        {
            account.MoneyOut(amount);
        }
    }

    public class Invoker
    {
        private Command command;

        public void SetCommand(Command command)
        {
            this.command = command;
        }

        public void ExecuteCommand()
        {
            command.Execute();
        }
    }
View Code

客戶端代碼:

技術分享圖片
class Program
    {
        static void Main(string[] args)
        {
            // 創建銀行帳號
            Account account = new Account();
            // 創建一個存入500元的命令
            Command commandIn = new MoneyInCommand(account,500);
            // 創建一個調度者
            BankAccount.Invoker invoker = new BankAccount.Invoker();

            // 設置存錢命令
            invoker.SetCommand(commandIn);
            // 執行
            invoker.ExecuteCommand();
            Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));

            // 再次存入500
            Command commandIn2 = new MoneyInCommand(account, 500);
            invoker.SetCommand(commandIn2);
            invoker.ExecuteCommand();
            Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));

            // 取出300
            Command commandOut = new MoneyOutCommand(account, 300);
            invoker.SetCommand(commandOut);
            invoker.ExecuteCommand();
            Console.WriteLine("The current amount is " + account.GetTotalAmout().ToString("N2"));

            Console.Read();
        }
    }
View Code

 執行結果

  技術分享圖片

設計模式(七) 命令模式