设计模式(七):命令模式

这个模式我在书上(JavaScript设计模式)的那个章节来来回回看了几遍,有两个关键点并没有说清楚,导致尽管表面上是很简单明了,但本质没有说清,以下是书上的例子。

书中淡解

例子(改前):

var carManager = {
     // request information
     requestInfo: function( model, id ){
       return "The information for " + model + " with ID " + id + " is foobar";
     },
     // purchase the car
     buyVehicle: function( model, id ){
       return "You have successfully purchased Item " + id + ", a " + model;
     },
     // arrange a viewing
     arrangeViewing: function( model, id ){
       return "You have successfully booked a viewing of " + model + " ( " + id + " ) ";
     }
};
carManager.arrangeViewing("Ferrari", "14523" );
carManager.requestInfo("Ford Mondeo", "54323" );
carManager.requestInfo("Ford Escort", "34232" );
carManager.buyVehicle("Ford Escort", "34232" );

例子(改后,命名模式):

carManager.execute = function ( name ) {
    return carManager[name] && carManager[name].apply( carManager, [].slice.call(arguments, 1) );
};
 
carManager.execute( "arrangeViewing", "Ferrari", "14523" );
carManager.execute( "requestInfo", "Ford Mondeo", "54323" );
carManager.execute( "requestInfo", "Ford Escort", "34232" );
carManager.execute( "buyVehicle", "Ford Escort", "34232" );

我想不明白,凭什么加上这个方法,就得到了优化,哪里就解耦了。

当carManager的API改变时候,调用这些API的对象当然都需要做修改,无论是改前、或者改后的代码写法。

PS:所以感觉例子不当。

网上见解

我带着疑问,放下书本,在网上寻找资料。我发现JavaScript实现的命令模式,都是类似上面的,而其他面向对象语言的实现,就是正正经经的按照模式的类图(如下)。于是,我就奇怪了,为什么JavaScript和面向对象的例子区别这么大,按理说模式应该与实现语言无关,特别是理论部分,更没有说到一块去(大部分)。

  • 客户(Client)角色:创建了一个具体命令(ConcreteCommand)对象并确定其接收者。

  • 命令(Command)角色:声明了一个给所有具体命令类的抽象接口。这是一个抽象角色。

  • 具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合;实现Execute()方法,负责调用接收考的相应操作。Execute()方法通常叫做执方法。

  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关的方法叫做行动方法。

  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

例子:

public interface Command {
    public void execute();
}
 
public class ConcreteCommand implements Command {
 
    private Receiver receiver = null;
    private String state;
 
    public ConcreteCommand(Receiver receiver){
      this.receiver = receiver;
    }
    public void execute() {
      receiver.action();
    }
}
 
public class Receiver {
    public void action(){
      //真正执行命令操作的功能代码
    }
}
 
public class Invoker {
    private Command command = null;
 
    public void setCommand(Command command) {
      this.command = command;
    }
 
    public void runCommand() {
      command.execute();
    }
}
 
public class Client {
    public void assemble(){
      //创建接收者
      Receiver receiver = new Receiver();
      //创建命令对象,设定它的接收者
      Command command = new ConcreteCommand(receiver);
      //创建Invoker,把命令对象设置进去
      Invoker invoker = new Invoker();
      invoker.setCommand(command);
    }
}

我的见解

首先命令(Command)模式是行为设计模式。

定义:

命令模式是将方法调用、请求或操作(Receiver类里的方法)封装到一个中间者(command类),供调用者(Invoker)调用。调用者不需要知道接收者的任何接口。

例子:

Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作。

将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式。换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了。

简而言之,本来是调用者直接调接收者代码的,不过Command模式在两者间加了一层中间者,中间者封装接收者动作,调用者直接调中间者。

使用场景:

1. 图形界面的菜单命令。

2. 需要支持命令的撤消(undo)。

优点:

1. 解耦了发送者和接受者之间联系。发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。

2. 它能实现Undo功能。每个具体命令都可以记住它刚刚执行的动作,并且在需要时恢复。

缺点:

滥用设计模式的带来的弊端而已。

 

总结

在仔细查阅资料后,关于上面面向对象语言和JavaScript的各自实现例子,我认为面向对象语言是更为准确,而JavaScript是不恰当的。JavaScript的例子根本没有做到接收者与调用者的解耦,虽然carManager.execute看起来像中间者,但调用者需要知道接收者里面的接口,这里是耦合了。所以,JavaScript实例看看就算了,并不正确

另外有篇文中讲的,设计模式一个"通病":好象喜欢将简单的问题复杂化,喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性、可维护性、还有复用性。这一点也有同感。

 

参考文献

1. http://www.jdon.com/designpatterns/command.htm (设计模式之Command

2. http://www.cnblogs.com/zhenyulu/articles/69858.html (命令(Command)模式

 

本文为原创文章,转载请保留原出处,方便溯源,如有错误地方,谢谢指正。
posted @ 2016-06-21 21:23  海角在眼前  阅读(610)  评论(0编辑  收藏  举报