设计模式之--命令模式

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

命令模式通过对命令的封装,将命令的请求(调用者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逻辑开发的责任分割,从而实现设计更加专业化。副作用就是需要增加了很多类和接口。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值