Libon

命令模式(Command Pattern) - 设计模式

设计模式之命令模式

使用命令模式我们可以将执行某个任务的对象与调用该方法的对象解耦

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

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

  placeOrder(order, id) {
    this.orders.push(id)
    return `您已经成功创建了订单 ${order} (${id})`;
  }

  trackOrder(id) {
    return `您的订单 ${id} 将在 20 分钟后抵达`
  }

  cancelOrder(id) {
    this.orders = this.orders.filter(order => order.id !== id)
    return `您已经取消了您的订单 ${id}`
  }
}

在实例对象上OrderManager,我们可以访问placeOrder,trackOrdercancelOrder方法。

const manager = new OrderManager();

manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");

但是,直接在实例上调用manager方法也有缺点。因为我们可能会在之后的开发中重命名某些方法,或者某些方法的某些功能发生某些变化。

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

现在让我们重构一下OrderManager类:它不再具有placeOrdercancelOrdertrackOrder方法,而是只有一个方法:execute。该方法将执行它给出的任何回调函数。每个回调都可以访问orders参数,所以我们将其作为第一个参数传递:

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);
    return `您已经成功创建了订单 ${order} (${id})`;
  });
}

function CancelOrderCommand(id) {
  return new Command((orders) => {
    orders = orders.filter((order) => order.id !== id);
    return `您已经取消了您的订单 ${id}`;
  });
}

function TrackOrderCommand(id) {
  return new Command(() => `您的订单 ${id} 将在 20 分钟后抵达`);
}

非常完美,现在这些方法不再直接耦合到OrderManager实例上,而是一个个独立的、解耦的函数,我们可以通过OrderManager上的execute方法调用它们。

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(`您已经成功创建了订单 ${order} (${id})`);
  });
}

function CancelOrderCommand(id) {
  return new Command(orders => {
    orders = orders.filter(order => order.id !== id);
    console.log(`您已经取消了您的订单 ${id}`);
  });
}

function TrackOrderCommand(id) {
  return new Command(() =>
    console.log(`您的订单 ${id} 将在 20 分钟后抵达`)
  );
}

const manager = new OrderManager();

manager.execute(new PlaceOrderCommand("Pad Thai", "1234"));
manager.execute(new TrackOrderCommand("1234"));
manager.execute(new CancelOrderCommand("1234"));

优点

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

缺点

命令模式的用例非常有限,并且经常向应用程序添加不必要的代码。如果您只是想在对象上调用一个方法,那么命令模式可能不是最佳选择。

cd ../