怎么用命令模式来实现外卖平台的订单管理器?
背景介绍
这是学习设计模式的第十五章,学习的是命令模式内容。记录的是自己学习理解的过程,欢迎大家讨论。
关于设计模式前十四节的内容,可以关注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
这个类中,我们可以直接调用placeOrder,trackOrder,cancelOrder来执行任务。
const manager = new OrderManager();
manager.placeOrder("Pad Thai", "1234");
manager.trackOrder("1234");
manager.cancelOrder("1234");
但是直接通过OrderManager
实例对象来调用这些方法是有一些弊端
的。比如说我们要在这些方法里扩展新功能,或者重命名这些方法,都会影响已有的调用逻辑。
命令模式
此时,我们可以用命令模式来改造,我们可以将这些方法与OrderManager
分离,并为每个功能创建单独的命令函数!下面我们来重构OrderManager类,不要placeOrder,trackOrder,cancelOrder函数,而只有一个函数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方法,传入命令就可以对订单执行相关操作了。
总结:
命令模式可以解耦函数和执行函数的对象。命名模式在处理有时效的函数,或者需要排队并在特定时间执行的函数时,会有更强的可控性。
命令模式的使用场景有限,并且往往需要添加不必要的样板文件。
相关活动
转载自:https://juejin.cn/post/7211775098311000125