[JS设计模式]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.

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

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!

在OrderManager类中,可以访问placeOrder、trackOrder和cancelOrder方法。直接使用这些方法将是完全有效的JavaScript !

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.

直接通过manager实例调用这些方法也有潜在的问题。以后可能决定重命名某些方法,或者这些方法的功能发生了变化。

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!

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

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.

让我们重构OrderManager类:代替placeOrdercancelOrdertrackOrder方法,它将只有一个方法:execute。这个方法将执行给定的任何命令。

Each command should have access to the orders of the manager, which we'll pass as its first argument.

每个命令都应该能够访问管理器的订单,我们将把它作为第一个参数传递给execute

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.

完美!不是将方法直接耦合到OrderManager实例,它们现在是分离的、解耦的函数,我们可以通过OrderManager上可用的execute方法调用它们。

优点

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.

命令模式允许我们将方法与执行该方法的对象解耦。如果您正在处理具有特定生命周期的命令,或者应该在特定时间排队并执行的命令,那么它可以为您提供更多的控制。

缺点

The use cases for the command pattern are quite limited, and often adds unnecessary boilerplate to an application.

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

完整代码

js 复制代码
class OrderManager {
  constructor() {
    this.orders = [];
  }

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

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

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

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

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

const manager = new OrderManager();

manager.execute(new PlaceOrderCommand("Pad Thai", "1234"));
manager.execute(new TrackOrderCommand("1234"));
manager.execute(new CancelOrderCommand("1234"));
相关推荐
清粥油条可乐炸鸡3 分钟前
el-transfer穿梭框数据量过大的解决方案
前端·javascript
独立开阀者_FwtCoder8 分钟前
TypeScript 是怎么工作的?一文带你深入编译器内部流程
前端·javascript·面试
独立开阀者_FwtCoder13 分钟前
前端自适应方案全面解析:打造多端适配的现代网页
前端·javascript·面试
渣渣宇a24 分钟前
Three_3D_Map 中国多个省份的组合边界绘制,填充背景
前端·javascript·three.js
狗子的狗粮28 分钟前
Node.js 模块加载与 exports 和 module.exports 的区别
javascript
zayyo29 分钟前
Web 应用轻量化实战
前端·javascript·面试
kovli33 分钟前
红宝书第十七讲:通俗详解JavaScript的Promise与链式调用
前端·javascript
李是啥也不会39 分钟前
Vue中Axios实战指南:高效网络请求的艺术
前端·javascript·vue.js
贾公子1 小时前
MySQL数据库基础 === 约束
前端·javascript
贾公子1 小时前
element ui & plus 版本 日期时间选择器的差异
前端·javascript