意圖
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶進行參數(shù)化漾橙;對請求排隊或記錄請求日志杈帐,以及支持可撤銷的操作闰蛔。
結(jié)構(gòu)
以及它們之間的交互關(guān)系:
動機
有時必須向某個對象提交請求僵控,但并不知道被請求的操作或請求的接收者的任何信息香到。命令模式通過將請求本身變成一個對象使得可以向未指定的應(yīng)用對象提出請求。這個對象可以被存儲并像其他對象一樣被傳遞喉祭。
適用性
- 抽象出待執(zhí)行的動作以參數(shù)化某對象养渴。你可用過程語言中的回調(diào)函數(shù)表達這種參數(shù)化機制;
- 在不同的時刻指定泛烙、排列和執(zhí)行請求。一個Command對象可以有一個與初始請求無關(guān)的生存期翘紊;
- 支持取消操作蔽氨。Command的Excute操作可在實施操作前將狀態(tài)存儲起來,在取消操作時這個狀態(tài)用來消除該操作的影響帆疟。Command接口必須添加一個Unexecute操作鹉究,該操作取消上一次Execute調(diào)用的效果;
- 支持修改日志踪宠,這樣當(dāng)系統(tǒng)崩潰時自赔,這些修改可以被重做一遍;
- 構(gòu)建基于原語操作的高級操作的系統(tǒng)柳琢。 這種結(jié)構(gòu)在支持事務(wù)的信息系統(tǒng)中很常見绍妨。 事務(wù)(Transaction)封裝了一組數(shù)據(jù)更改。 Command模式提供了一種事務(wù)建模的方法柬脸。 Command有一個公共的接口他去,讓你以相同的方式調(diào)用所有的事務(wù)。 該模式還可以輕松地使用新的事務(wù)來擴展系統(tǒng)倒堕。
優(yōu)點
- 命令對象(Command)的調(diào)用者與接收者完全解耦灾测;
- Command 子類極易擴展;
- 容易將多個命令裝配成一個復(fù)合命令垦巴;
示例
模擬一個圖形用戶界面的菜單欄媳搪,它們執(zhí)行請求響應(yīng)用戶輸入(打開文檔铭段、黏貼等操作)。
實現(xiàn)(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";
}
}
// 應(yīng)用程序
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("關(guān)閉「{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; } }
}
// 測試應(yīng)用程序
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」文檔