引言
使用命令模式,我们可以将执行特定任务的对象与调用该方法的对象解耦。
核心思想
命令模式的核心思想是将请求封装成一个对象,从而使请求的发起者和请求的执行者解耦。
这样,请求的发起者只需要知道如何创建命令对象并将其传递给请求者,而不需要知道命令对象的具体实现细节。
- 命令模式还支持可撤销操作,因为命令对象可以保存执行前的状态,并在需要时恢复状态。这样,可以通过撤销命令对象来撤销之前的操作。
适用场景
- 需要将请求发送者和接收者解耦的场景。
命令模式可以将请求封装成对象,从而使得请求的发送者和接收者之间解耦,使得系统更加灵活。
- 需要支持撤销和重做操作的场景。
命令模式可以将请求封装成对象,并提供撤销和重做操作,从而使得系统更加灵活和可靠。
- 需要支持事务操作的场景。
命令模式可以将多个请求封装成一个事务,从而保证事务的原子性和一致性。
- 需要支持日志和审计功能的场景。
命令模式可以将请求封装成对象,并记录请求的执行情况,从而支持日志和审计功能。
- 需要支持队列请求的场景。
命令模式可以将请求封装成对象,并将请求放入队列中,从而支持队列请求的场景。
案例实践
假设我们有一个简单的计算器,它可以执行加、减、乘、除四种操作。
我们可以使用命令模式来实现这个计算器,将每个操作封装成一个命令对象,然后将命令对象传递给计算器,从而实现不同的操作。
- 首先,我们定义一个命令接口,它包含一个execute方法:
class Command {
execute() {}
}
- 然后,我们定义四个具体的命令类,分别对应加、减、乘、除四种操作:
class AddCommand extends Command {
constructor(receiver, value) {
super();
this.receiver = receiver;
this.value = value;
}
execute() {
this.receiver.add(this.value);
}
}
class SubCommand extends Command {
constructor(receiver, value) {
super();
this.receiver = receiver;
this.value = value;
}
execute() {
this.receiver.sub(this.value);
}
}
class MulCommand extends Command {
constructor(receiver, value) {
super();
this.receiver = receiver;
this.value = value;
}
execute() {
this.receiver.mul(this.value);
}
}
class DivCommand extends Command {
constructor(receiver, value) {
super();
this.receiver = receiver;
this.value = value;
}
execute() {
this.receiver.div(this.value);
}
}
其中,每个命令类都包含一个接收者对象和一个操作值,execute方法会调用接收者对象的相应方法来执行操作。
- 最后,我们定义一个计算器类,它包含一个命令队列和一个执行命令的方法:
class Calculator {
constructor() {
this.commands = [];
this.current = 0;
}
addCommand(command) {
this.commands.push(command);
}
executeCommand() {
this.commands[this.current].execute();
this.current++;
}
undo() {
this.commands[this.current - 1].execute();
this.current--;
}
redo() {
this.commands[this.current].execute();
this.current++;
}
}
其中,addCommand方法用于将命令对象添加到命令队列中,executeCommand方法用于执行当前命令,undo方法用于撤销上一个命令,redo方法用于重做上一个命令。
- 使用这些类来实现一个简单的计算器:
const calculator = new Calculator();
const receiver = {
value: 0,
add: function (value) {
this.value += value;
console.log(`Add ${value}, result: ${this.value}`);
},
sub: function (value) {
this.value -= value;
console.log(`Sub ${value}, result: ${this.value}`);
},
mul: function (value) {
this.value *= value;
console.log(`Mul ${value}, result: ${this.value}`);
},
div: function (value) {
this.value /= value;
console.log(`Div ${value}, result: ${this.value}`);
},
};
const addCommand = new AddCommand(receiver, 10);
const subCommand = new SubCommand(receiver, 5);
const mulCommand = new MulCommand(receiver, 2);
const divCommand = new DivCommand(receiver, 4);
calculator.addCommand(addCommand);
calculator.addCommand(subCommand);
calculator.addCommand(mulCommand);
calculator.addCommand(divCommand);
calculator.executeCommand(); // Add 10, result: 10
calculator.executeCommand(); // Sub 5, result: 5
calculator.executeCommand(); // Mul 2, result: 10
calculator.executeCommand(); // Div 4, result: 2.5
calculator.undo(); // Div 4, result: 10
calculator.undo(); // Mul 2, result: 20
calculator.undo(); // Sub 5, result: 25
calculator.undo(); // Add 10, result: 35
calculator.redo(); // Add 10, result: 10
calculator.redo(); // Sub 5, result: 5
calculator.redo(); // Mul 2, result: 10
calculator.redo(); // Div 4, result: 2.5