对我们的下一本书感兴趣吗?了解更多关于 使用 React 构建大型 JavaScript Web 应用程序

设计模式

命令模式

使用 **命令模式**,我们可以 *解耦* 执行特定任务的对象和调用该方法的对象。

假设我们有一个在线食品外送平台。用户可以下单、跟踪和取消订单。

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 方法。直接使用这些方法在 JavaScript 中完全有效!

const manager = new OrderManager();

manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");

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

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

让我们重构 OrderManager 类:它不再包含 placeOrdercancelOrdertrackOrder 方法,而只有一个方法:execute。此方法将执行它收到的任何命令。

每个命令都应该可以访问 managerorders,我们将其作为第一个参数传递给它。

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

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

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

  • PlaceOrderCommand
  • CancelOrderCommand
  • TrackOrderCommand
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.`);
}

完美!我们不再将方法直接耦合到 OrderManager 实例上,而是将它们转换为单独的、解耦的函数,可以通过 OrderManager 上可用的 execute 方法调用它们。

index.js
1class OrderManager {
2 constructor() {
3 this.orders = [];
4 }
5
6 execute(command, ...args) {
7 return command.execute(this.orders, ...args);
8 }
9}
10
11class Command {
12 constructor(execute) {
13 this.execute = execute;
14 }
15}
16
17function PlaceOrderCommand(order, id) {
18 return new Command(orders => {
19 orders.push(id);
20 console.log(`You have successfully ordered ${order} (${id})`);
21 });
22}
23
24function CancelOrderCommand(id) {
25 return new Command(orders => {
26 orders = orders.filter(order => order.id !== id);
27 console.log(`You have canceled your order ${id}`);
28 });
29}
30
31function TrackOrderCommand(id) {
32 return new Command(() =>
33 console.log(`Your order ${id} will arrive in 20 minutes.`)
34 );
35}
36
37const manager = new OrderManager();
38
39manager.execute(new PlaceOrderCommand("Pad Thai", "1234"));
40manager.execute(new TrackOrderCommand("1234"));
41manager.execute(new CancelOrderCommand("1234"));

优点

命令模式使我们能够将方法与执行该操作的对象解耦。如果您处理具有特定生命周期的命令,或者应该在特定时间排队和执行的命令,它将为您提供更多控制权。

缺点

命令模式的用例非常有限,并且通常会为应用程序增加不必要的样板代码。


参考资料