command-pattern

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Command Pattern

Command Pattern

With the Command Pattern, we can decouple objects that execute a certain task from the object that calls the method.
Let's say we have an online food delivery platform. Users can place, track, and cancel orders.
借助Command Pattern,我们可以将执行特定任务的对象与调用方法的对象解耦。
假设我们有一个在线外卖平台,用户可以下单、跟踪订单和取消订单。

When to Use

适用场景

  • Use this when you need to decouple the object invoking an operation from the object performing it
  • This is helpful when commands need a certain lifespan or should be queued and executed at specific times
  • 当你需要将调用操作的对象与执行操作的对象解耦时,可以使用该模式
  • 当命令需要具备一定生命周期,或者需要排队并在特定时间执行时,该模式会很有用

Instructions

实现步骤

  • Create a Command class with an
    execute
    method that encapsulates the action
  • Replace direct method calls with command objects passed to a single
    execute
    method on the manager
  • Use this pattern sparingly as it can add unnecessary boilerplate in simpler JavaScript applications
  • 创建一个包含
    execute
    方法的Command类,该方法封装具体操作
  • 将直接调用方法的方式替换为向管理器的单个
    execute
    方法传入命令对象
  • 在简单的JavaScript应用中应谨慎使用该模式,因为它可能会增加不必要的冗余代码

Details

详细实现

js
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}`
  }
}
On the
OrderManager
class, we have access to the
placeOrder
,
trackOrder
and
cancelOrder
methods. It would be totally valid JavaScript to just use these methods directly!
js
const manager = new OrderManager();

manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");
However, there are downsides to invoking the methods directly on the
manager
instance. It could happen that we decide to rename certain methods later on, or the functionality of the methods change.
Say that instead of calling it
placeOrder
, we now rename it to
addOrder
! This would mean that we would have to make sure that we don't call the
placeOrder
method anywhere in our codebase, which could be very tricky in larger applications. Instead, we want to decouple the methods from the
manager
object, and create separate command functions for each command!
Let's refactor the
OrderManager
class: instead of having the
placeOrder
,
cancelOrder
and
trackOrder
methods, it will have one single method:
execute
. This method will execute any command it's given.
Each command should have access to the
orders
of the manager, which we'll pass as its first argument.
js
class OrderManager {
  constructor() {
    this.orders = [];
  }

  execute(command, ...args) {
    return command.execute(this.orders, ...args);
  }
}
We need to create three
Command
s for the order manager:
  • PlaceOrderCommand
  • CancelOrderCommand
  • TrackOrderCommand
js
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.`);
}
Perfect! Instead of having the methods directly coupled to the
OrderManager
instance, they're now separate, decoupled functions that we can invoke through the
execute
method that's available on the
OrderManager
.
js
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
类中,我们可以调用
placeOrder
trackOrder
cancelOrder
方法。直接使用这些方法是完全合法的JavaScript写法!
js
const manager = new OrderManager();

manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");
不过,直接在
manager
实例上调用方法存在弊端。比如后续我们可能会重命名某些方法,或者修改方法的功能。
比如我们把
placeOrder
重命名为
addOrder
!这意味着我们必须确保代码库中没有任何地方再调用
placeOrder
方法,这在大型应用中会非常棘手。相反,我们希望将方法与
manager
对象解耦,为每个命令创建独立的命令函数!
让我们重构
OrderManager
类:不再保留
placeOrder
cancelOrder
trackOrder
方法,而是只保留一个方法:
execute
。该方法会执行传入的任何命令。
每个命令都需要访问管理器的
orders
属性,我们会将其作为第一个参数传入。
js
class OrderManager {
  constructor() {
    this.orders = [];
  }

  execute(command, ...args) {
    return command.execute(this.orders, ...args);
  }
}
我们需要为订单管理器创建三个
Command
  • PlaceOrderCommand
  • CancelOrderCommand
  • TrackOrderCommand
js
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
方法来调用它们。

Pros

优点

The command pattern allows us to decouple methods from the object that executes the operation. It gives you more control if you're dealing with commands that have a certain lifespan, or commands that should be queued and executed at specific times.
Command Pattern允许我们将方法与执行操作的对象解耦。当处理具有一定生命周期的命令,或者需要排队并在特定时间执行的命令时,它能让你获得更多控制权。

Cons

缺点

The use cases for the command pattern are quite limited, and often adds unnecessary boilerplate to an application.
Command Pattern的适用场景相当有限,而且通常会给应用增加不必要的冗余代码。

Source

来源

References

参考资料