原文:https://addyosmani.com/resources/essentialjsdesignpatterns/book/#commandpatternjavascript
命令模式旨在将函数的调用、请求或操作封装到一个简单对象中,同时赋予我们参数化和传递方法调用的能力,我们可以随时来执行这些调用。另外,它能使我们能够将调用动作的对象和实现它们的对象分离开,在替换具体的类(对象)时有更大的整体灵活性。
Concrete 类最好使用基于类的编程语言来解释,并且它与 abstract 类的概念相关。abstract 类是的定义一个接口,但是不必提供它所有成员方法的具体实现。它充当基类,可以派生出其他类。实现了这些缺失的功能的派生类就被称作 concrete 类。
命令模式背后的思想是,它为我们提供了一种方式,可以将发出命令的职责和执行命令分离,并将这个责任委托给其他不同的对象。
实现方面,简单的命令对象将动作和希望调用动作的对象绑定到一起。它们都会包含一个执行操作(如 run()
或者 execute()
)。所有的命令对象保持一致的接口会让它们在需要相互替换时更容易,这也被认为是这个模式最大的好处之一。
为了演示命令模式,我们将创建一个简单的购物车服务。
(function() {
var carManager = {
// 请求信息
requestInfo: function(model, id) {
return 'The information for ' + model + ' with ID ' + id + ' is foobar';
},
// 购买汽车
buyVehicle: function(model, id) {
return 'You have successfully purchased Item ' + id + ', a ' + model;
},
// 安排观看
arrangeViewing: function(model, id) {
return (
'You have successfully booked a viewing of ' +
model +
' ( ' +
id +
' ) '
);
}
};
})();
看看上面的代码,一般来说我们都是直接通过访问对象来执行我们 ourManager
的方法。技术上,我们这么做并不会觉得有什么问题,他完全是合法有效的 JavaScript 代码。然而某些情况下,它却是有缺陷的。
例如,想象一下如果 carManager
内部的核心 API 发生了变更。我们应用内所有直接访问这些方法的对象都需要跟着变更。这可以看作是一层耦合,这实际上违背了面向对象的尽量松耦合的的理念。相反,我们可以通过进一步抽象 API 的解决这个问题。
现在让我们来拓展我么我们的 carManager
,我们的使用命令模式可以把它改造成这个样子:接受任何可以在 carManager
对象上执行的命名的方法,传递任何可能会用到的数据,如:车的型号和 ID。
下面就是我们希望最终看到的效果:
carManager.execute('buyVehicle', 'Ford Escort', '453543');
根据这个结构,我们现在应该将 carManager.execute
方法按照如下方式定义:
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');