意圖
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可撤銷的操作。
結構
命令模式結構圖
以及它們之間的交互關系:
命令模式協作圖
動機
有時必須向某個對象提交請求,但并不知道被請求的操作或請求的接收者的任何信息。命令模式通過將請求本身變成一個對象使得可以向未指定的應用對象提出請求。這個對象可以被存儲并像其他對象一樣被傳遞。
適用性
- 抽象出待執(zhí)行的動作以參數化某對象。你可用過程語言中的回調函數表達這種參數化機制;
- 在不同的時刻指定、排列和執(zhí)行請求。一個Command對象可以有一個與初始請求無關的生存期;
- 支持取消操作。Command的Excute操作可在實施操作前將狀態(tài)存儲起來,在取消操作時這個狀態(tài)用來消除該操作的影響。Command接口必須添加一個Unexecute操作,該操作取消上一次Execute調用的效果;
- 支持修改日志,這樣當系統崩潰時,這些修改可以被重做一遍;
- 構建基于原語操作的高級操作的系統。 這種結構在支持事務的信息系統中很常見。 事務(Transaction)封裝了一組數據更改。 Command模式提供了一種事務建模的方法。 Command有一個公共的接口,讓你以相同的方式調用所有的事務。 該模式還可以輕松地使用新的事務來擴展系統。
優(yōu)點
- 命令對象(Command)的調用者與接收者完全解耦;
- Command 子類極易擴展;
- 容易將多個命令裝配成一個復合命令;
示例
模擬一個圖形用戶界面的菜單欄,它們執(zhí)行請求響應用戶輸入(打開文檔、黏貼等操作)。
實現(C#)
命令模式示例結構圖
using System;
using System.Collections.Generic;
public abstract class Command
{
public abstract void Execute();
}
// 文檔粘貼的命令
public class PasteCommand : Command
{
private Document document;
public PasteCommand(Document document)
{
this.document = document;
}
public override void Execute()
{
this.document.Paste();
}
}
// 打開文檔的命令
public class OpenCommand : Command
{
private Application application;
public OpenCommand(Application application)
{
this.application = application;
}
public override void Execute()
{
string name = AskUser();
Document document = new Document(name);
this.application.Add(document);
document.Open();
}
private string AskUser()
{
return "New Document";
}
}
// 應用程序
public class Application
{
private List<Document> documents = new List<Document>();
private Menu menu = new Menu();
public void Add(Document document)
{
this.documents.Add(document);
}
public Menu Menu { get { return this.menu; } }
public List<Document> Documents { get { return this.documents; } }
}
// 文檔
public class Document
{
private string name;
public Document(string name)
{
this.name = name;
}
public void Open()
{
Console.WriteLine("打開「{0}」文檔", this.name);
}
public void Close()
{
Console.WriteLine("關閉「{0}」文檔", this.name);
}
public void Cut()
{
Console.WriteLine("剪貼「{0}」文檔", this.name);
}
public void Paste()
{
Console.WriteLine("粘貼「{0}」文檔", this.name);
}
public string Name { get { return this.name; } }
}
// 命令菜單
public class Menu
{
private Dictionary<string,MenuItem> items = new Dictionary<string, MenuItem>();
public void Add(MenuItem item)
{
this.items.Add(item.Name, item);
}
public MenuItem this[string name]
{
get { return this.items[name]; }
}
}
// 命令菜單項
public class MenuItem
{
private Command command;
private string name;
public MenuItem(string name)
{
this.name = name;
}
public void SetCommand(Command command)
{
this.command = command;
}
public void Clicked()
{
this.command.Execute();
}
public string Name { get { return this.name; } }
}
// 測試應用程序
public class App
{
public static void Main(string[] args)
{
Application application = new Application();
application.Menu.Add(new MenuItem("Open"));
application.Menu.Add(new MenuItem("Paste"));
// 模擬觸發(fā)"Open"菜單事件,將Open命令傳送給Open菜單項
application.Menu["Open"].SetCommand(new OpenCommand(application));
application.Menu["Open"].Clicked();
// 模擬觸發(fā)"Paste"菜單事件,將Paste命令傳送給Paste菜單項
application.Menu["Paste"].SetCommand(new PasteCommand(application.Documents[0]));
application.Menu["Paste"].Clicked();
}
}
// 控制臺輸出:
// 打開「New Document」文檔
// 粘貼「New Document」文檔