Skip to content

命令模式

约 971 字大约 3 分钟

设计模式

2018-04-15

命令模式 在前端领域的用例相对有限,这里仅做简单的介绍。

什么是命令模式?

Command(命令)模式 旨在将方法调用、请求或操作封装到单一对象中,从而根据我们不同的请求对客户进行参数化 和 传递可供执行的方法调用。这种模式将调用操作的对象与知道如何实现该操作的对象解耦,并在交换出具体类(对象) 方面提供更大的整体灵活性。

用基于类的变成语言解释 具体类 是最恰当的,它们与抽象类的思想有关。 一个抽象类定义一个结构,但不一定为它所有的成员函数提供实现。 它作为一个基类,派生出其他类。实现确实功能的派生类被称为一个 具体类

Command(命令)模式 背后的主要思想是:它为我们提供了一种分离职责的手段,这些职责包括从执行命令的任意地方 发布命令以及将该职责转而委托给不同对象。

实施明智的、简单那的命令对象将把 Action 动作和调用该动作的对象绑定在一起。它们始终包括一个执行操作 (如 run()execute())。所有具有相同接口的 Command 对象可以根据需要轻松交换,这被认为是该模式的一个更大的好处。

使用命令模式

假设我们有一个在线购物系统,用户可以下单、跟踪订单 和 取消订单。

class OrderManager {
  constructor() {
    this.orders = []
  }

  placeOrder(order, id) {
    this.orders.push(id)
    return `Successfully ordered ${order} (${id})`
  }

  trackOrder(id) {
    return `Order ${id} is in progress`
  }

  cancelOrder(id) {
    this.orders = this.orders.filter(order => order.id !== id)
    return `Order ${id} has been canceled`
  }
}

OrderManager 类中,我们可以访问 placeOrdertrackOrdercancelOrder 方法。

const manager = new OrderManager()

manager.placeOrder('shoes', '1234')
manager.trackOrder('1234')
manager.cancelOrder('1234')

但是,直接在 manager 实例上调用方法也有缺点。我们可能在未来的某些时候,重命名这些方法,或者某些功能发生变化, 或者移除它们。

假设我们把 placeOrder 方法重命名为 addOrder,这意味着我们需要确保在整个程序应用中没有任何位置调用 placeOrder 方法,这在大型的程序中可能是一个大问题。

我们希望将方法与管理者对象解耦,并为每个命令创建独立的命令函数!

我们开始对 OrderManager 类进行重构,移除 placeOrdertrackOrdercancelOrder 方法, 并添加一个新的 execute 方法,此方法将执行任何给它的命令。

每个命令都应该可以访问 manager 的 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 `Successfully ordered ${order} (${id})`
  })
}

function CancelOrderCommand(id) {
  return new Command((orders) => {
    orders = orders.filter(order => order.id !== id)
    return `Order ${id} has been canceled`
  })
}

function TrackOrderCommand(id) {
  return new Command(() => `Order ${id} is in progress`)
}

它们不再直接耦合到 OrderManager 实例,而是独立的、解耦的函数, 我们可以通过 OrderManager 上可用的 execute 方法调用它们。

class OrderManager {
  constructor() {
    this.orders = []
  }

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

const manager = new OrderManager()

manager.execute(new PlaceOrderCommand('shoes', '1234'))
manager.execute(new TrackOrderCommand('1234'))
manager.execute(new CancelOrderCommand('1234'))

优点

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

缺点

命令模式的用例非常有限,并且通常会向应用程序添加不必要的样板内容。