理解设计模式(十五)——命令模式

命令模式

提出问题

在编写程序的过程中,我们经常会让某个对象去调用其他对象的方法。有时候,我们可能会调用不同对象的方法,需要随时进行替换。比如说,我单击了界面上的一个按钮,按钮作为一个界面控件,可以具有不同的功能。有时候它被用作是提交按钮,有时候也可以是关闭按钮。如果我直接写死,这个按钮被点击之后是提交。那如果我需要一个关闭按钮,就需要全部重写(直接复制之前的代码加以改动也算全部重写,因为代码还是冗余的)。

怎么解决代码冗余的问题呢?

解决问题

想一想,我们需要改变的只是按钮被点击后的行为。那么,我们是不是可以把这一块封装起来,作为一个单独的对象?我们不妨叫他命令对象。

每次按钮被点击后,就去调用这个命令对象中预先设置好的方法。如果要更换按钮点击后的行为,只需要更换命令对象即可。

下面是代码示例:

class Window {
closeWindow() {
// 关闭窗口的函数
}
}
class Command {
constructor() {
this.window = new Winodw;
}
exec() {
window.closeWindow();
}
}
class Button {
constructor(command) {
this.command = command;
}
click() {
this.command.exec();
}
}

let closeWindowCommand = new Command();
let button = new Button(closeWindowCommand);
button.click();

命令队列

除了实现让一个对象发起不同的请求以外,有时候我们可能希望一个对象能发起一系列请求。并且在程序运行中,我们能动态的增加与删除这些请求。

这时候,我们可以把原来的命令改成一个命令队列(数组),每次请求时,逐个执行队列中的命令。

下面是个例子:

// 我们希望点击按钮后,能提交信息然后关闭窗口
class Submit {
submit() {
// 提交信息
}
}
class Window {
closeWindow() {
// 关闭窗口的函数
}
}
class Command {
constructor() {
this.window = new Winodw;
}
exec() {
window.closeWindow();
}
}
class Button {
constructor(command) {
this.commands = [command]; // 命令队列
}
addCommand(command) {
this.commands.push(command);
}
click() {
this.commands.forEach(command=>command.exec());
}
}

let closeWindowCommand = new Command();
let button = new Button(closeWindowCommand);
button.click();

实现Undo

只需要在Command上增加Undo方法即可。直接上例子:

// 比如计算器
class Calculator {
constructor() {
this.result = 0;
}
add(val) {
this.result += val;
}
sub(val) {
this.result -= val;
}
}
class AddCommand {
constructor(calculator) {
this.val = 0;
this.calculator = calculator;
}
exec(val) {
this.val = val;
this.calculator.add(val)
}
undo() {
this.calculator.sub(this.val);
}
}

class Button {
constructor(command) {
this.command = command;
}
click() {
this.command.exec();
}
}

let cal = new Calculator();
let command = new AddCommand(cal);
let button = new Button(command);
button.click();
Author: LeoB_O
Link: https://leob-o.github.io/2019/06/12/CommandPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.