使用命令模式,可以将执行某个任务的对象与调用该方法的对象解耦。

假设有一个外卖配送平台。用户可以下单、跟踪和取消订单。

class OrderManager() {
  constructor() {
    this.orders = []
  }

  placeOrder(order, id) {
    this.orders.push(id)
    return `You have successfully ordered ${order} (${id})`;
  }

  trackOrder(id) {
    return `Your order ${id} will arrive in 20 minutes.`
  }

  cancelOrder(id) {
    this.orders = this.orders.filter(order => order.id !== id)
    return `You have canceled your order ${id}`
  }
}

OrderManager 类上,可以访问 placeOrdertrackOrdercancelOrder 方法。直接使用这些方法将是完全可行的。

但是,直接在 manager 实例上调用方法也有缺点。我们可能会决定稍后重命名某些方法,或者改变某些方法的功能。

假设现在将其重命名为 addOrder ,而不是 placeOrder ,这意味着我们必须确保代码库中的没有任何位置调用 placeOrder 方法,这种场景在大型应用程序中可能非常棘手。相反,我们希望将方法与 manager 对象解耦,并为每个命令创建单独的命令函数。

让我们重构 OrderManager 类:它不再具有 placeOrdercancelOrdertrackOrder 方法,而是只有一个方法: execute 。该方法将执行它给出的任何命令。

class OrderManager {
  constructor() {
    this.orders = [];
  }

  execute(command, ...args) {
    return command.execute(this.orders, ...args);
  }
}

还需要为订单管理器创建三个 Command

class Command {
  constructor(execute) {
    this.execute = execute;
  }
}

function PlaceOrderCommand(order, id) {
  return new Command((orders) => {
    orders.push(id);
    return `You have successfully ordered ${order} (${id})`;
  });
}

function CancelOrderCommand(id) {
  return new Command((orders) => {
    orders = orders.filter((order) => order.id !== id);
    return `You have canceled your order ${id}`;
  });
}

function TrackOrderCommand(id) {
  return new Command(() => `Your order ${id} will arrive in 20 minutes.`);
}

const manager = new OrderManager();
manager.execute(new PlaceOrderCommand("noodle", "1234"));
manager.execute(new TrackOrderCommand("1234"));
manager.execute(new CancelOrderCommand("1234"));

这些方法不再直接耦合到 OrderManager 实例,而是现在是独立的解耦函数,我们可以通过 OrderManager 方法调用它们。

优点

命令模式允许我们将方法从执行操作的对象中解耦。它能够更加灵活的控制,比如一些具有生命周期的命令,又或者需要在特定时间排队并执行的命令。

缺点

命令模式的用例非常有限,并且经常向应用程序添加不必要的样板。