likes
comments
collection
share

怎么用命令模式来实现外卖平台的订单管理器?

作者站长头像
站长
· 阅读数 18

背景介绍

这是学习设计模式的第十五章,学习的是命令模式内容。记录的是自己学习理解的过程,欢迎大家讨论。

关于设计模式前十四节的内容,可以关注React设计模式专栏获取更多内容。

极简释义

命令模式:解耦方法,通过发送命令执行任务。

正文开始

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

比如说,一个外卖送餐平台,用户可以下订单跟踪订单和取消订单,这时我们可以建一个OrderManager来管理订单:

常规实现

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}`
  }
}

OrderManager这个类中,我们可以直接调用placeOrdertrackOrdercancelOrder来执行任务。

const manager = new OrderManager();
 
manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");

但是直接通过OrderManager实例对象来调用这些方法是有一些弊端的。比如说我们要在这些方法里扩展新功能,或者重命名这些方法,都会影响已有的调用逻辑。

命令模式

此时,我们可以用命令模式来改造,我们可以将这些方法OrderManager分离,并为每个功能创建单独的命令函数!下面我们来重构OrderManager类,placeOrdertrackOrdercancelOrder函数,而只有一个函数execute,这个方法可以执行他接受到的任何命令

class OrderManager {
  constructor() {
    this.orders = [];
  }
 
  execute(command, ...args) {
    return command.execute(this.orders, ...args);
  }
}

然后要实现用户下单跟踪取消订单的功能,我们来创建三个命令

  • 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) => {
    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.`);
}

现在我们就把功能函数OrderManager对象进行分离,接下来我们来看怎么使用

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"));

通常在项目中,我们的OrderManager是内容会比较,其中涉及用户支付时效等诸多方面,这时把操作订单的函数单独抽离出来,会让功能更加单一;而操作订单的函数可以通过new Command根据需要进行扩展,而不会影响已有的功能;最后通过调用OrderManager实例对象manager.execute方法,传入命令就可以对订单执行相关操作了。

总结:

命令模式可以解耦函数和执行函数的对象。命名模式在处理有时效的函数,或者需要排队并在特定时间执行的函数时,会有更强的可控性

命令模式的使用场景有限,并且往往需要添加不必要的样板文件

相关活动