当前位置:编程学习 > C#/ASP.NET >>

设计模式之--命令模式

 

命令模式的意图一是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;二是对请求排队或记录请求日志,以及支持可撤消的操作。简略图如下:

\

 

 

命令模式通过对命令的封装,将命令的请求(调用者Invoker)和执行(接收者Receiver)进行了责任分离,委派给不同的对象,不仅使得调用者和执行者之间实现了解耦(命令的请求方就不需要知道接收方的接口,也不需要知道命令是如何执行的具体情况),还使得可以记录命令的执行记录,添加执行日志,使得命令的控制、执行、取消和重做变得容易。

 

Delphi的Action就采用了这种模式,其中Windows控件(比如按钮,菜单)是调用者,Action是命令,而接收者是Action的OnExecute事件的实现者。当然,Delphi为了实现设计期间或者运行期间控件的变灰,控件的Caption能在需要时与Action保持一致,在中间加了一个ActionLink类层次结构,采用的是桥模式,控件将控件与Action之间的通信交给ActionLink来完成,而AcionLink对象的创建则采用的是工厂方法(工厂就是控件本身),只不过它利用了GetActionLinkClass函数做得更加巧妙。而Action的具体执行采用事件的方式,又使得命令和接收者进一步解耦,这样命令就不需要知道接收者的接口,也不用维护对接收者的引用,接收者也不需要知道命令的具体细节,只需要提供一个符合要求的事件处理方法即可。

 

下面是示例代码:

 

/// <summary>

/// Command类,定义一个执行操作的接口,也可以是一个接口。

/// </summary>

public abstract class Command_Command

{

  protected Command_Receiver _Receiver;

  public abstract void Execute();

  public Command_Receiver Receiver

  {

   get

   {

    return _Receiver;

   }

   set

   {

    _Receiver = value;

   }

  }

}

public class Command_ConcreateCommand : Command_Command

    {

  public Command_ConcreateCommand()

  {

  }

  public override void Execute()

  {

   if(System.Windows.Forms.MessageBox.Show("你想执行操作么?","系统提示!",

    System.Windows.Forms.MessageBoxButtons.YesNo)==System.Windows.Forms.DialogResult.Yes)

   {

    if(this._Receiver!=null)

    {

     this._Receiver.Action();

    }

   }

  }

 

    }

public class Command_Receiver

{

  public Command_Receiver()

  {

  }

  public void Action()

  {

   System.Windows.Forms.MessageBox.Show("Command Execute!");

  }

}

public class Command_Invoker

{

  private Command_Command command;

  public Command_Invoker()

  {

  }

  public void SetCommand(Command_Command command)

  {

   this.command = command;

  }

  public void Execute()

  {

   this.command.Execute();

  }

}

public class Command_Client

{

  public static void Test()

  {

   //建立具体命令

   Command_ConcreateCommand command = new Command_ConcreateCommand();

   //建立具体接收者

   Command_Receiver r1 = new Command_Receiver();

   //链接命令与接收者

   command.Receiver = r1;

            //创建调用者

   Command_Invoker invoker = new Command_Invoker();

   //设置调用者的命令子类

   invoker.SetCommand(command);

   //执行调用操作

   invoker.Execute();

  }

}

 

总结:

 

     如果没有将命令的请求和执行进行责任分离并委托给不同的对象,那么请求与执行都需要在同一个对象内完成,比如Button的Click命令,但这就带来一个问题,Button的Click逻辑太复杂,因为同样是Button,不同的用户请求Click命令时执行的业务逻辑可能完全不同,而且通过Button的子类化来实现也根本不现实。所以,命令的请求和执行的责任必须分离为好,要实现分离有两种办法,一是采用回调函数(原来的Windows系统比较普遍),二是采用事件或委托(Delphi对象方法),三就是采用命令模式。1,2实际上属于同一类型的处理方式,好处是简单(后记:这种应用模式虽然比较普遍,但还有一个缺点就是页面设计者和页面逻辑实现很难分离,比如dotnet的aspx页面和对应的.cs之间的耦合由于太紧密,很难实现UI设计和UI逻辑实现的分离,silverlight的VM就是为了弥补这个缺陷.)。但缺点是没法对命令本身进行管理(执行,取消,日志,重做等)。如果命令本身不需要管理和控制,使用2比较好,现在的界面控件的命令处理基本都是采用这种方式进行。如果需要管理命令本身就要采用Command模式。

 

     如果对接收者进行抽象,就可以实现命令和接收者的动态组合,这有点类似装饰模式,策略模式。

 

     对命令模式的一个改进就是可以将命令中接收者引用除掉,利用事件或者委托的办法与接受者发生联系。如果再对调用者进行抽象,这样就形成了一个调用者体系和命令体系,如果需要两者的联系可再增加一个联系层,这样就有3个相对独立的层次体系,Delphi的Action模式就是这样的。这样做有利于系统的可视化设计。

 

后记:命令模式在现在的编程体系中,使用非常广泛,特别是在UI层与控制层或者业务逻辑层之间的解耦合方面作用非常大,当然,不好的地方是增加了系统的复杂度。silverlight编程中的mvvm模式中的vm层其实就可以看做是一个命令层,由这个层衔接M和V两层,同时这个层与页面层得衔接不再是传统的页面和.cs的关系,而是采用直接在UI中进行绑定的方式进行(传统的方式是将页面事件绑定到对应的后台.cs文件,这个文件在mvvm模式中也有,除了缺省的代码和挂接VM的代码外,已经没什么代码,如果采用动态绑定V-VM,基本就只剩下缺省代码了,因此UI设计完全不用考虑对这个文件的影响),这就使得页面设计和页面实现逻辑分离,可以很好的实现UI设计和UI逻辑开发的责任分割,从而实现设计更加专业化。副作用就是需要增加了很多类和接口。

补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,