mcpField
C
command-pattern

command-pattern

github.com/patternsdev开发257646 次安装28 周前更新
代码仓库

关于 command-pattern

这是一份 JavaScript 设计模式指南,介绍如何用命令对象将操作执行与调用方分离,适合需要独立管理或排队执行命令的开发者,也说明了不宜采用该模式的情形。

主要功能

  1. 01将调用方与操作逻辑分离
  2. 02用命令对象封装操作
  3. 03通过统一的 execute 方法分发命令
  4. 04支持排队或定时执行

适用场景

  1. 01将订单操作排队以便稍后执行
  2. 02更改操作实现而不改动调用方
  3. 03简单的一次性调用避免使用该模式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/patternsdev/skills --skill command-pattern

安装后在 Claude Code 中输入/command-pattern确认已加载。

详细介绍

命令模式

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

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

适用场景

  • 当你需要将调用某项操作的对象与执行该操作的对象解耦时,可以使用此模式
  • 当命令需要持续一段时间,或需要排队并在特定时间执行时,此模式会很有帮助

不适用场景

  • 对于不需要撤销/重做、排队或日志记录的简单一次性操作
  • 当直接调用函数已经足够清晰,而额外的抽象只会增加复杂度、没有带来好处时
  • 当系统中的操作很少,而命令基础设施会造成过度设计时

说明

  • 创建一个带有 execute 方法的 Command 类,用于封装操作
  • 用传递给管理器的单个 execute 方法的命令对象,替代直接的方法调用
  • 谨慎使用此模式,因为它可能会给简单的 JavaScript 应用带来不必要的样板代码

详情

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)
    return `You have canceled your order ${id}`
  }
}

在 OrderManager 类中,我们可以使用 placeOrder、trackOrder 和 cancelOrder 方法。直接使用这些方法完全符合 JavaScript 的语法!

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,我们会将它作为第一个参数传入。

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) => {
    const index = orders.indexOf(id);
    if (index > -1) orders.splice(index, 1);
    return `You have canceled your order ${id}`;
  });
}

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

很好!现在,这些方法不再与 OrderManager 实例直接耦合,而是成为独立、解耦的函数,我们可以通过 OrderManager 上的 execute 方法调用它们。

优点

命令模式可以将方法与执行操作的对象解耦。如果你处理的命令有一定的生命周期,或者需要排队并在特定时间执行,那么它能让你获得更多控制权。

缺点

命令模式的适用场景相当有限,而且通常会给应用带来不必要的样板代码。

来源

参考资料

评论

支持 Markdown