1. 程式人生 > >二十三種設計模式[14] - 命令模式(Command Pattern)

二十三種設計模式[14] - 命令模式(Command Pattern)

前言

       命令模式,物件行為型模式的一種。它幫助我們將功能的呼叫者與實現者之間解耦(甚至完全解耦)。呼叫者與實現者之間並不是直接引用關係,呼叫者只需要知道如何傳送當前功能的請求即可,而不用關心該請求由誰在何時完成。

       “ 將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。”     

                                                                                                                                                             ——《設計模式 - 可複用的面向物件軟體》

結構

Connand_1

  • Command(命令類介面):宣告執行操作的介面;
  • ConcreteCommand(命令類):Command介面的實現,用來呼叫具體的實現;
  • Invoker(呼叫者):用來控制命令的執行,個人理解為Command的代理類;
  • Receiver(接收者):功能具體的實現,由ConcreteCommand呼叫;

示例

       考慮一個能夠控制各種智慧家電的App。在這個App中使用者可以隨意新增按鈕來控制某個家電的某個功能。也就是說當我們開發這個App時並不能確定使用者新增的按鈕是控制什麼家電執行什麼功能。現在我們使用命令模式來模擬

Connand_2

/// <summary>
/// 命令的接收者
/// </summary>
public class Television
{
    public void Open()
    {
        Console.WriteLine("開啟電視機");
    }

    public void Close()
    {
        Console.WriteLine("關閉電視機");
    }
}

/// <summary>
/// 命令介面
/// </summary>
public interface ICommand
{
    void Execute();
}

/// <summary>
/// 開機命令
/// </summary>
public class TvOpenCommand : ICommand
{
    private Television _tv = null;

    public TvOpenCommand(Television tv)
    {
        this._tv = tv;
    }

    public void Execute()
    {
        this._tv.Open();
    }
}

/// <summary>
/// 關機命令
/// </summary>
public class TvCloseCommand : ICommand
{
    private Television _tv = null;

    public TvCloseCommand(Television tv)
    {
        this._tv = tv;
    }

    public void Execute()
    {
        this._tv.Close();
    }
}

/// <summary>
/// 命令的呼叫者
/// </summary>
public class Button
{
    public ICommand Command { set; get; }

    public Button(ICommand command)
    {
        this.Command = command;
    }

    public void Click()
    {
        this.Command.Execute();
    }
}

static void Main(string[] args)
{            
    Television tv = new Television();           //建立電視機物件(命令的接收者)

    ICommand tvOpen = new TvOpenCommand(tv);    //建立開機命令
    ICommand tvClose = new TvCloseCommand(tv);  //建立關機命令

    Button button = new Button(tvOpen);         //建立開機按鈕(命令的呼叫者)
    button.Click();                             //執行命令

    button.Command = tvClose;                   //將按鈕功能變更為關機
    button.Click();                             //執行命令

    Console.ReadKey();
}

       在上述示例中,Button類充當呼叫者角色,Television類充當接收者角色。我們為Television類中的每一個函式都建立了命令類,不同的命令類決定了不同的操作,而該操作具體的實現由接收者完成。作為呼叫者的Button類並不知道它使用了哪個類執行了哪些操作,它只知道在它的Click函式中呼叫了ICommand介面的Execute函式。這就體現了命令模式的本質,將呼叫者與接收者解耦

  • 命令巨集

           在日常生活中,我們往往希望通過一個按鈕來執行一系列操作(比如一鍵開啟電視和空調)。這個時候可以將命令模式與組合模式一同使用來實現一個命令巨集(又稱命令佇列)。

Connand_3

/// <summary>
/// 命令的接收者
/// </summary>
public class Television
{
    public void Open()
    {
        Console.WriteLine("開啟電視機");
    }

    public void Close()
    {
        Console.WriteLine("關閉電視機");
    }
}

/// <summary>
/// 命令接收者
/// </summary>
public class AirConditioner
{
    public void Open()
    {
        Console.WriteLine("開啟空調");
    }

    public void Close()
    {
        Console.WriteLine("關閉空調");
    }
}

/// <summary>
/// 命令介面
/// </summary>
public interface ICommand
{
    void Execute();
}

/// <summary>
/// 開機命令
/// </summary>
public class TvOpenCommand : ICommand
{
    private Television _tv = null;

    public TvOpenCommand(Television tv)
    {
        this._tv = tv;
    }

    public void Execute()
    {
        this._tv.Open();
    }
}

/// <summary>
/// 關機命令
/// </summary>
public class TvCloseCommand : ICommand
{
    private Television _tv = null;

    public TvCloseCommand(Television t
    {
        this._tv = tv;
    }

    public void Execute()
    {
        this._tv.Close();
    }
}

/// <summary>
/// 開機命令
/// </summary>
public class AcOpenCommand : ICommand
{
    private AirConditioner _ac = null;

    public AcOpenCommand(AirConditioner ac)
    {
        this._ac = ac;
    }

    public void Execute()
    {
        this._ac.Open();
    }
}

/// <summary>
/// 關機命令
/// </summary>
public class AcCloseCommand : ICommand
{
    private AirConditioner _ac = null;

    public AcCloseCommand(AirConditioner ac)
    {
        this._ac = ac;
    }

    public void Execute()
    {
        this._ac.Close();
    }
}

/// <summary>
/// 命令巨集
/// </summary>
public class MacorCommand : ICommand
{
    public List<ICommand> Commands { set; get; } = new List<ICommand>();

    public void Execute()
    {
        foreach (var command in this.Commands)
        {
            command.Execute();
        }
    }
}

/// <summary>
/// 命令的呼叫者
/// </summary>
public class Button
{
    public ICommand Command { set; get; }

    public Button(ICommand command)
    {
        this.Command = command;
    }

    public void Click()
    {
        this.Command.Execute();
    }
}

static void Main(string[] args)
{
    Television tv = new Television();                       //電視物件(命令接收者)
    AirConditioner ac = new AirConditioner();               //空調物件(命令接收者)

    TvOpenCommand tvOpenCommand = new TvOpenCommand(tv);    //電視開機命令
    AcOpenCommand acOpenCommand = new AcOpenCommand(ac);    //空調開機命令
    MacorCommand macorCommand = new MacorCommand();         //巨集命令
    macorCommand.Commands.Add(tvOpenCommand);               //設定巨集命令
    macorCommand.Commands.Add(acOpenCommand);               //設定巨集命令

    Button button = new Button(macorCommand);               //建立巨集按鈕
    button.Click();                                         //執行命令

    Console.ReadKey();
}

      示例中建立一個聚合了ICommand介面的MacorCommand類充當組合模式中的Compsite角色,用來儲存一系列的命令。它的存在能夠使呼叫者一次執行多個使用不同接收者的命令。

      對於命令模式的擴充套件還有很多,比如請求日誌和逆向操作(撤銷)等等。這裡就不一一舉例了。

總結

       命令模式的核心思想是將一個請求封裝,將一個請求命令的發出(呼叫)和接收處理分割開,達到將呼叫者與接收者解耦的目的。命令模式中的每個命令類都保持了一個很小的顆粒度,因為它只封裝了一個接收類中的一個函式。好處是在開發的過程中呼叫者不必關心也不知道具體執行的函式,保證了呼叫者與接收者的鬆耦合狀態,以便更好的控制和應對各種變化。同時也意味著令類需要封裝的函式越多命令類也就越多,存在類爆炸的風險。


       以上,就是我對命令模式的理解,希望對你有所幫助。

       示例原始碼:https://gitee.com/wxingChen/DesignPatternsPractice

       系列彙總:https://www.cnblogs.com/wxingchen/p/9833744.html

       本文著作權歸本人所有,如需轉載請標明本文連結(https://www.cnblogs.com/wxingchen/p/10031585.html)