[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"));
相关推荐
Python私教19 分钟前
YggJS RLogin暗黑霓虹主题登录注册页面 版本:v0.1.1
开发语言·javascript·ecmascript
南半球与北海道#1 小时前
el-table合并单元格
javascript·vue.js·elementui·表格合并
Jimmy1 小时前
客户端存储 - IndexedDB
前端·javascript·indexeddb
华仔啊1 小时前
别学23种了!Java项目中最常用的6个设计模式,附案例
java·后端·设计模式
成小白1 小时前
前端实现两个页面之间的通讯
前端·javascript
啷咯哩咯啷1 小时前
element-plus el-tree-v2大数据量勾选节点卡顿问题
前端·javascript·vue.js
EndingCoder2 小时前
测试 Next.js 应用:工具与策略
开发语言·前端·javascript·log4j·测试·全栈·next.js
前端李二牛3 小时前
Vue3 特性标志
前端·javascript
烛阴4 小时前
TypeScript 函数重载入门:让你的函数签名更精确
前端·javascript·typescript
Keya4 小时前
MacOS端口被占用的解决方法
前端·后端·设计模式