原文:https://addyosmani.com/resources/essentialjsdesignpatterns/book/#commandpatternjavascript

    命令模式旨在将函数的调用、请求或操作封装到一个简单对象中,同时赋予我们参数化和传递方法调用的能力,我们可以随时来执行这些调用。另外,它能使我们能够将调用动作的对象和实现它们的对象分离开,在替换具体的类(对象)时有更大的整体灵活性。

    Concrete 类最好使用基于类的编程语言来解释,并且它与 abstract 类的概念相关。abstract 类是的定义一个接口,但是不必提供它所有成员方法的具体实现。它充当基类,可以派生出其他类。实现了这些缺失的功能的派生类就被称作 concrete 类。

    命令模式背后的思想是,它为我们提供了一种方式,可以将发出命令的职责和执行命令分离,并将这个责任委托给其他不同的对象。

    实现方面,简单的命令对象将动作和希望调用动作的对象绑定到一起。它们都会包含一个执行操作(如 run() 或者 execute() )。所有的命令对象保持一致的接口会让它们在需要相互替换时更容易,这也被认为是这个模式最大的好处之一。

    为了演示命令模式,我们将创建一个简单的购物车服务。

    1. (function() {
    2. var carManager = {
    3. // 请求信息
    4. requestInfo: function(model, id) {
    5. return 'The information for ' + model + ' with ID ' + id + ' is foobar';
    6. },
    7. // 购买汽车
    8. buyVehicle: function(model, id) {
    9. return 'You have successfully purchased Item ' + id + ', a ' + model;
    10. },
    11. // 安排观看
    12. arrangeViewing: function(model, id) {
    13. return (
    14. 'You have successfully booked a viewing of ' +
    15. model +
    16. ' ( ' +
    17. id +
    18. ' ) '
    19. );
    20. }
    21. };
    22. })();

    看看上面的代码,一般来说我们都是直接通过访问对象来执行我们 ourManager 的方法。技术上,我们这么做并不会觉得有什么问题,他完全是合法有效的 JavaScript 代码。然而某些情况下,它却是有缺陷的。

    例如,想象一下如果 carManager 内部的核心 API 发生了变更。我们应用内所有直接访问这些方法的对象都需要跟着变更。这可以看作是一层耦合,这实际上违背了面向对象的尽量松耦合的的理念。相反,我们可以通过进一步抽象 API 的解决这个问题。

    现在让我们来拓展我么我们的 carManager ,我们的使用命令模式可以把它改造成这个样子:接受任何可以在 carManager 对象上执行的命名的方法,传递任何可能会用到的数据,如:车的型号和 ID。

    下面就是我们希望最终看到的效果:

    1. carManager.execute('buyVehicle', 'Ford Escort', '453543');

    根据这个结构,我们现在应该将 carManager.execute 方法按照如下方式定义:

    1. carManager.execute = function(name) {
    2. return (
    3. carManager[name] &&
    4. carManager[name].apply(carManager, [].slice.call(arguments, 1))
    5. );
    6. };

    我们最终示例的调用将变成下面这个样子:

    1. carManager.execute('arrangeViewing', 'Ferrari', '14523');
    2. carManager.execute('requestInfo', 'Ford Mondeo', '54323');
    3. carManager.execute('requestInfo', 'Ford Escort', '34232');
    4. carManager.execute('buyVehicle', 'Ford Escort', '34232');