动机:在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合――比如需要对行为进行“记录、撤销/重做(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
意图:将一个请求封装为一个对象,从而使你可用不同的请求对客户(程序,行为请求者)进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
基本Code:
public class Document
{
public void ShowText()
{
}
}
public class Graphics
{
public void DisplayGraphic()
{
}
}
//应用程序主干/高层抽象
public class App
{
public void Show()
{
Document doc=new Document();
doc.ShowText(); //直接依赖具体行为实现
Graphics graph=new Graphics();
graph.DisplayGraphic();
}
}
使用Command模式的Code如下:
//具体实现细节
public class Document
{
public void ShowText()
{
}
}
public class Graphics
{
public void DisplayGraphic()
{
}
}
//实现Command模式
public interface ICommand
{
void Show();
void Undo();
void Redo();
}
//具体化的命令对象――从抽象的意义来讲,DocumentCommand表示一个行为
public class DocumentCommand:ICommand
{
Document document;
public DocumentCommand(Document doc)
{
this.document=doc;
}
public void Show ()
{
document.ShowText();
}
public void Undo()
{
}
public void Redo()
{
}
}
//具体化的命令对象――从抽象的意义来讲,GraphicsCommand表示一个行为
public class GraphicsCommand:ICommand
{
Graphics graph;
public GraphicsCommand(Graphics graph)
{
this.graph=graph;
}
public void Show()
{
graph.DisplayGraphic();
}
public void Undo()
{
}
public void Redo()
{
}
}
//应用程序主干/高层抽象
public class App
{
Stack<ICommand> stack;
Stack<ICommand> undoList;
public void Show()
{
foreach(ICommand cmd in stack)
{
cmd.Show();
}
}
public void Undo()
{
if (canUndo)
{
ICommand cmd=stack.Pop();
cmd.Undo();
undoList.Add(cmd);
}
}
public void Redo()
{
if (canRedo)
{
ICommand cmd=undoList.Pop();
cmd.Redo();
}
}
}
Command模式结构图如下:
Command模式要点:
1、 Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言训,常见的实现手段是“将行为抽象为对象”。
2、 实现Command接口的具体命令对象ConcreateCommand有时候根据需要可能会保存一些额外的状态信息。
3、 通过使用Composite模式,可以将多个“命令”封装为一个“复合命令”MacroCommand。
4、 Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口――实现”来定义接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱。