耦合與變化
耦合是軟體不能抵禦變化災難的根本性原因。不僅實體物件與實體物件之間存在耦合關係,實體物件與行為操作之間也存在耦合關係。
動機(Motivation)
在軟體構建過程中,“行為要求者”與“行為實現者”通常呈現一種“緊耦合”。但在某些場合——比如需要對行為進行“記錄、撤銷/重做(undo/redo)、事務”等處理,這種無法抵禦變化的緊耦合是不合適的。
在這種情況下,如何將“行為要求者”與“行為實現者”解耦?將一組行為抽象為對象,可以實現二者之間的松耦合。
意圖(Intent)
將一個請求封裝為一個對象,從而使你可用不同的請求對客戶[行為要求者]進行參數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。
——《設計模式》GoF
結構
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
namespace Command
{
Command 抽象介面#region Command 抽象介面
public interface Command
{
void Show();
void Undo();
void Redo();
}
#endregion
//表示一個行為
ConcreteCommand#region ConcreteCommand
public class DocumentCommand : Command
{
Document document;
public DocumentCommand(Document doc)
{
this.document = doc;
}
public void Show()
{
document.ShowTest();
}
public void Undo()
{
}
public void Redo()
{
}
public class GraphicsCommand : Command
{
Graphics graphics;
public GraphicsCommand(Graphics grap)
{
this.graphics = grap;
}
public void Show()
{
graphics.ShowTest();
}
public void Undo()
{
}
public void Redo()
{
}
}
}
#endregion
Receiver 低層模組#region Receiver 低層模組
public class Document
{
public void ShowTest()
{
Console.WriteLine("Document");
}
public void Undo()
{
}
public void Redo()
{
}
}
public class Graphics
{
public void ShowTest()
{
Console.WriteLine("Graphics");
}
public void Undo()
{
}
public void Redo()
{
}
}
#endregion
Client Application 高層模組#region Client Application 高層模組
class Application
{
Stack<Command> stack;
public void Show()
{
foreach (Command c in stack)
{
c.Show();
}
}
static void Main(string[] args)
{
}
}
#endregion
}
Command模式的幾個要點
• Command模式的根本目的在於將“行為要求者”與“行為實現者” 解耦,在物件導向語言中,常見的實現手段是“將行為抽象為對象”。
• 實現Command介面的具體命令對象ConcreteCommand有時候根據需要可能會儲存一些額外的狀態資訊。
• 通過使用Composite模式,可以將多個“命令”封裝為一個“複合命令”MacroCommand。
• Command模式與C#中的Delegate有些類似。但兩者定義行為介面的規範有所區別:Command以物件導向中的“介面-實現”來定義行為介面規範,更嚴格,更符合抽象原則;Delegate以函數簽名來定義行為介面規範,更靈活,但抽象能力比較弱。