一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情。
简介:
Command模式 :将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
(图画的有些许垃圾,大家勿怪😅)
生活例子类比:
- 客人到餐厅点了一道锅包肉吃。这里的客人就是请求者,接受者就是厨师,接受到做锅包肉的命令去做这道菜。而命令类就是服务员,他不需要关心客人要吃什么,也不需要关心是哪位厨师做锅包肉,他只需要将客人的请求(要吃什么菜)记录下来,并且告知给厨师就可以了。
- 客人与厨师之间是完全解耦的,客人告知服务员,服务员在告知厨师。
- 而且会存在有很多客人,发起很多请求,也可能会存在客人发起请求,又突然临时有事不要这道菜了。这就是command模式可以实现的对请求排队、记录、以及支持可撤销的操作。
示例程序:
一个画图软件,用户拖动鼠标是程序会绘制出红色圆点,点击clear按钮后会清除所有圆点。(图解设计模式一书中的例子)
//Commond接口
class Command {
execute() {
};
}
//绘制一个点的命令,实现Command接口
class DrawCommand extends Command {
execute() {
console.log("draw")
}
}
// 宏命令:一组命令集合(命令模式与组合模式的产物)
// 发布者发布一个请求,命令对象会遍历命令集合下的一系列子命令并执行,完成多任务。
class MacroCommand extends Command {
constructor() {
super()
this.commands = [];
}
execute() {
let cmdIterator = this.commands[Symbol.iterator]();
let it = {
done: false
};
while (!it.done) {
it = cmdIterator.next();
if (!it.done) {
it.value.execute();
}
}
}
append(cmd) {
if (cmd) {
this.commands.push(cmd)
}
}
undo() {
if (this.commands.length) {
this.commands.pop()
}
}
clear() {
this.commands = []
}
}
//接受者:接受命令去执行
class DrawCanvas {
constructor(history) {
this.history = history
}
paint() {
this.history.execute();
}
}
//测试
let cmd1 = new DrawCommand();
let cmd2 = new DrawCommand();
let cmd3 = new DrawCommand();
//单个命令
let drawCanvas1 = new DrawCanvas(cmd1);
drawCanvas1.paint();
//宏命令
let history = new MacroCommand();
history.append(cmd1);
history.append(cmd2);
history.append(cmd3);
let drawCanvas2 = new DrawCanvas(history);
drawCanvas2.paint();
角色:
-
命令:Command类 -
具体的命令:-
画红色圆点:DrawCommand类 -
清除所有圆点:MacroCommand类
-
-
接受者:DrawCanvas类 -
请求者:用户(这里是调用的时候就为请求者) -
发动者:开始执行命令的角色(这里是调用的时候也为发动者)
应用场景:
- 系统需要在不同的时间指定请求、将请求排队和执行请求。
- 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
- 系统需要将一组操作组合在一起,即支持宏命令
优点:
- 将调用命令的对象与和如何实现命令的对象解耦
- command扩展性高,对请求可进行排队或日志记录。(支持撤销,队列,宏命令等功能)。
缺点:
- 额外增加命令对象太多时会使这个类膨胀得非常大