技术开发 频道

行为型模式篇-命令模式(Command Pattern)


一般情况下我们使用这个类的时候,都会这样去写:
class Program { static void Main(string[] args) { Document doc = new Document(); doc.Display(); doc.Undo(); doc.Redo(); } }
    这样的使用本来是没有任何问题的,但是我们看到在这个特定的应用中,出现了Undo/Redo的操作,这时如果行为的请求者和行为的实现者之间还是呈现这样一种紧耦合,就不太合适了。可以看到,客户程序是依赖于具体Document的命令(方法)的,引入Command模式,需要对Document中的三个命令进行抽象,这是Command模式最有意思的地方,因为在我们看来Display(),Undo(),Redo()这三个方法都应该是Document所具有的,如果单独抽象出来成一个命令对象,那就是把函数层面的功能提到了类的层面,有点功能分解的味道,我觉得这正是Command模式解决这类问题的优雅之处,先对命令对象进行抽象:


                   图4

示意性代码:

/// <summary> /// 抽象命令 /// </summary> public abstract class DocumentCommand { Document _document; public DocumentCommand(Document doc) { this._document = doc; } /**//// <summary> /// 执行 /// </summary> public abstract void Execute(); }
其他的具体命令类都继承于该抽象类,如下:

                                图5

示意性代码:

/**//// <summary> /// 显示命令 /// </summary> public class DisplayCommand : DocumentCommand { public DisplayCommand(Document doc) : base(doc) { } public override void Execute() { _document.Display(); } } /**//// <summary> /// 撤销命令 /// </summary> public class UndoCommand : DocumentCommand { public UndoCommand(Document doc) : base(doc) { } public override void Execute() { _document.Undo(); } } /**//// <summary> /// 重做命令 /// </summary> public class RedoCommand : DocumentCommand { public RedoCommand(Document doc) : base(doc) { } public override void Execute() { _document.Redo(); } }
    现在还需要一个Invoker角色的类,这其实相当于一个中间角色,前面我曾经说过,使用这样的一个中间层也是我们经常使用的手法,即把A对B的依赖转换为A对C的依赖。如下:

                                                                       图6

示意性代码:

/// <summary> /// Invoker角色 /// </summary> public class DocumentInvoker { DocumentCommand _discmd; DocumentCommand _undcmd; DocumentCommand _redcmd; public DocumentInvoker(DocumentCommand discmd,DocumentCommand undcmd,DocumentCommand redcmd) { this._discmd = discmd; this._undcmd = undcmd; this._redcmd = redcmd; } public void Display() { _discmd.Execute(); } public void Undo() { _undcmd.Execute(); } public void Redo() { _redcmd.Execute(); } }
现在再来看客户程序的调用代码:

class Program { static void Main(string[] args) { Document doc = new Document(); DocumentCommand discmd = new DisplayCommand(doc); DocumentCommand undcmd = new UndoCommand(doc); DocumentCommand redcmd = new RedoCommand(doc); DocumentInvoker invoker = new DocumentInvoker(discmd,undcmd,redcmd); invoker.Display(); invoker.Undo(); invoker.Redo(); } }
    可以看到:

1.在客户程序中,不再依赖于Document的Display(),Undo(),Redo()命令,通过Command对这些命令进行了封装,使用它的一个关键就是抽象的Command类,它定义了一个操作的接口。同时我们也可以看到,本来这三个命令仅仅是三个方法而已,但是通过Command模式却把它们提到了类的层面,这其实是违背了面向对象的原则,但它却优雅的解决了分离命令的请求者和命令的执行者的问题,在使用Command模式的时候,一定要判断好使用它的时机。

2.上面的Undo/Redo只是简单示意性的实现,如果要实现这样的效果,需要对命令对象设置一个状态,由命令对象可以把状态存储起来。


0
相关文章