command-pattern
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCommand 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 method that encapsulates the action
execute - Replace direct method calls with command objects passed to a single method on the manager
execute - Use this pattern sparingly as it can add unnecessary boilerplate in simpler JavaScript applications
- 创建一个包含方法的Command类,该方法封装具体操作
execute - 将直接调用方法的方式替换为向管理器的单个方法传入命令对象
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 class, we have access to the , and methods. It would be totally valid JavaScript to just use these methods directly!
OrderManagerplaceOrdertrackOrdercancelOrderjs
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 instance. It could happen that we decide to rename certain methods later on, or the functionality of the methods change.
managerSay that instead of calling it , we now rename it to ! This would mean that we would have to make sure that we don't call the method anywhere in our codebase, which could be very tricky in larger applications. Instead, we want to decouple the methods from the object, and create separate command functions for each command!
placeOrderaddOrderplaceOrdermanagerLet's refactor the class: instead of having the , and methods, it will have one single method: . This method will execute any command it's given.
OrderManagerplaceOrdercancelOrdertrackOrderexecuteEach command should have access to the of the manager, which we'll pass as its first argument.
ordersjs
class OrderManager {
constructor() {
this.orders = [];
}
execute(command, ...args) {
return command.execute(this.orders, ...args);
}
}We need to create three s for the order manager:
CommandPlaceOrderCommandCancelOrderCommandTrackOrderCommand
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 instance, they're now separate, decoupled functions that we can invoke through the method that's available on the .
OrderManagerexecuteOrderManagerjs
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}`
}
}在类中,我们可以调用、和方法。直接使用这些方法是完全合法的JavaScript写法!
OrderManagerplaceOrdertrackOrdercancelOrderjs
const manager = new OrderManager();
manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");不过,直接在实例上调用方法存在弊端。比如后续我们可能会重命名某些方法,或者修改方法的功能。
manager比如我们把重命名为!这意味着我们必须确保代码库中没有任何地方再调用方法,这在大型应用中会非常棘手。相反,我们希望将方法与对象解耦,为每个命令创建独立的命令函数!
placeOrderaddOrderplaceOrdermanager让我们重构类:不再保留、和方法,而是只保留一个方法:。该方法会执行传入的任何命令。
OrderManagerplaceOrdercancelOrdertrackOrderexecute每个命令都需要访问管理器的属性,我们会将其作为第一个参数传入。
ordersjs
class OrderManager {
constructor() {
this.orders = [];
}
execute(command, ...args) {
return command.execute(this.orders, ...args);
}
}我们需要为订单管理器创建三个:
CommandPlaceOrderCommandCancelOrderCommandTrackOrderCommand
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.`);
}完美!现在方法不再直接与实例耦合,而是变成了独立的解耦函数,我们可以通过上的方法来调用它们。
OrderManagerOrderManagerexecutePros
优点
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
参考资料
- Command Design Pattern - SourceMaking
- Command Pattern - Refactoring Guru
- Command Pattern - Carlos Caballero
- Command Design Pattern - SourceMaking
- Command Pattern - Refactoring Guru
- Command Pattern - Carlos Caballero