客戶端只是想要發出命令或者請求,不關心請求的真正接收者是誰,也不關心具體如何實現,而且同一個請求的動作可以有不同的請求內容,當然具體的處理功能也不一樣,請問該怎麼實現?下面我們來學習命令模式
目的
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作。
結構圖
詳細解析
Command:
定義命令的接口,聲明執行的方法。
ConcreteCommand:
命令接口實現對象,是“虛”的實現;通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。
Receiver:
接收者,真正執行命令的對象。任何類都可能成為一個接收者,只要它能夠實現命令要求實現的相應功能。
Invoker:
要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令對象的入口。
Client:
創建具體的命令對象,並且設置命令對象的接收者。注意這個不是我們常規意義上的客戶端,而是在組裝命令對象和接收者,或許,把這個Client稱為裝配者會更好理解,因為真正使用命令的客戶端是從Invoker來觸發執行。
對應代碼
[csharp]
<span style="font-size:18px;">class Program
{
static void Main(string[] args)
{
Receiver r = new Receiver();
Command c = new ConcreteCommand(r);
Invoker i = new Invoker();
// Set and execute command
i.SetCommand(c);
i.ExecuteCommand();
Console.Read();
}
}
//用來聲明執行操作的接口
abstract class Command
{
protected Receiver receiver;
public Command(Receiver receiver)
{
this.receiver = receiver;
}
abstract public void Execute();
}
//ConcreteCommand類,將一個接收者對象綁定於一個操作,調用接收者相應的操作,以實現Execute
class ConcreteCommand : Command
{
public ConcreteCommand(Receiver receiver)
:
base(receiver) { }
public override void Execute()
{
receiver.Action();
}
}
//知道如何實施與執行一個與請求相關的操作,任何類都可能作為一個接收者
class Receiver
{
public void Action()
{
Console.WriteLine("執行請求!");
}
}
//要該命令執行這個請求
class Invoker
{
private Command command;
public void SetCommand(Command command)
{
this.command = command;
}
public void ExecuteCommand()
{
command.Execute();
}
}</span>
生活中得例子
Command模式將一個請求封裝為一個對象,從而使不同的請求對客戶進行參數化,用餐時的賬單是Command模式的一個例子,服務員接受顧客的點單,把它記在賬單上封裝,這個點單被排隊等待做,賬單不依賴菜單的,它可以被不同的顧客使用,也可以添加入不同的點單項目。
Command模式實例剖析
Command模式它封裝的是命令,把命令的發出者的責任和命令執行者的責任分開,我們知道,一個類是一組操作和相應變量的結合,在訂餐的過程中有如下一個類Waiter
[csharp]
<span style="font-size:18px;"></span>
[csharp]
<span style="font-size:18px;">//服務員
public class Waiter
{
private IList<Command> orders = new List<Command>();
//設置訂單
public void SetOrder(Command command)
{
if (command.ToString() == "命令模式.BakeChickenWingCommand")
{
Console.WriteLine("服務員:雞翅沒有了,請點別的燒烤。");
}
else
{
orders.Add(command);
Console.WriteLine("增加訂單:" + command.ToString() + " 時間:" + DateTime.Now.ToString());
}
}
//取消訂單
public void CancelOrder(Command command)
{
orders.Remove(command);
Console.WriteLine("取消訂單:" + command.ToString() + " 時間:" + DateTime.Now.ToString());
}
//通知全部執行
public void Notify()
{
foreach (Command cmd in orders)
{
cmd.ExcuteCommand();
}
}
}</span>
下面我們得抽象一個抽象的命令,Setorder、CancelOrder、等方法都是Command所具有的,如果單獨出來一個命令對象,那就把函數層面的功能提到了類的層面,對命令的抽象是很重要之處。,其他具體的方法命令類都繼承於該抽象類。
[csharp]
<span style="font-size:18px;">//抽象命令
public abstract class Command
{
protected Barbecuer receiver;
public Command(Barbecuer receiver)
{
this.receiver = receiver;
}
//執行命令
abstract public void ExcuteCommand();
}
//烤羊肉串命令
class BakeMuttonCommand : Command
{
public BakeMuttonCommand(Barbecuer receiver)
: base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.BakeMutton();
}
}
//烤雞翅命令
class BakeChickenWingCommand : Command
{
public BakeChickenWingCommand(Barbecuer receiver)
: base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.BakeChickenWing();
}
}
</span>
[csharp]
<span style="font-size:18px;">//烤肉串者
public class Barbecuer
{
public void BakeMutton()
{
Console.WriteLine("烤羊肉串!");
}
public void BakeChickenWing()
{
Console.WriteLine("烤雞翅!");
}
}
</span>
客戶端的調用
[csharp]
<span style="font-size:18px;"> static void Main(string[] args)
{
//開店前的准備
Barbecuer boy = new Barbecuer();
Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);
Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);
Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);
Waiter girl = new Waiter();
//開門營業 顧客點菜
girl.SetOrder(bakeMuttonCommand1);
girl.SetOrder(bakeMuttonCommand2);
girl.SetOrder(bakeChickenWingCommand1);
//點菜完閉,通知廚房
girl.Notify();
Console.Read();
}</span>
在客戶端的程序中,不依賴於Waiter的SetOrder、CancelOrder、Notify、修改訂單等命令,通過Command對這些命令進行了封裝,使得它的一個關鍵抽象類是Command類,它定義了一個操作的接口,同時我們可以看到,這三個命令是方法而已,但是通過Command模式把它們提到了類的層面,雖然違背了面向對象的原則,但它解決了分離命令的請求者和命令的執行者的問題。
Command命令的時機
1:需要在不同的時間指定請求、將請求排隊。
2:系統需要支持命令的撤銷。
3:如果一個系統要將系統中所有的數據更新到日志裡,以便在系統崩潰時,可以根據日志裡讀回所有的數據更新命令,重新調用Execute()方法一條一條執行這些命令,從而恢復系統在崩潰前所做的數據更新。
命令模式的作用
1:它很容易的設計一個命令隊列
2:在需要的情況下,可以教容易的將命令計入日志
3:允許接收請求的一方決定是否要否決請求
4:可以很容易的實現對請求的撤銷和重做
5:由於加緊新的具體命令類不影響其它的類,因此增加新的具體命令類很容易,命令模式把請求的一個操作的對象與知道怎麼執行一個操作的對象分隔開。
總結
Command模式輕松的實現了“行為請求者”與“行為實現者”的解耦