执行命令时,发布者 和 执行者 分开,中间加入命令对象,作为中转站;
适用场景:
- 解耦发送者与接收者:请求发送者 和 请求接收者( 执行者 ) 需要 解耦,发送者 与 接收者 之间 不直接进行交互;
- 抽象行为:需要将 等待执行 的行为 抽象出来;
优点:
- 降低耦合:将 请求调用者 与 请求接收者 进行 解耦;
- 扩展性高:如果要 扩展新命令,直接 定义 新的命令对象 即可;如果要 执行一组命令,发送一组命令 给接收者 即可;
缺点:
- 增加复杂度:扩展命令 会 导致 类的数量增加,增加了 系统实现的复杂程度;
- 需要针对每个命令,都要开发一个与之对应的命令类;
类图
三种角色
- Receiver 接受者角色:该角色就是干活的角色,命令传递到这里是应该被执行的;
- Command 命令角色:需要执行的所有命令都在这里声明;
- Invoker 调用者角色:接收到命令,并执行命令;
代码
// 接受者角色
class Cooker {
cook() {
console.log(`做饭`);
}
}
// 接受者角色
class Cleaner {
clean() {
console.log(`清洁`);
}
}
// 命令角色
class CookCommand {
constructor(cooker) {
this.cooker = cooker;
}
execute() {
this.cooker.cook();
}
}
// 命令角色
class CleanCommand {
constructor(cleaner) {
this.cleaner = cleaner;
}
execute() {
this.cleaner.clean();
}
}
// 命令角色
class Customer {
constructor(command) {
this.command = command;
}
cook() {
this.command.execute();
}
clean() {
this.command.execute();
}
}
let command = new CookCommand(new Cooker());
let c = new Customer(command);
c.cook();
command = new CleanCommand(new Cleaner());
c = new Customer(command);
c.clean();
应用场景
计数器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计数器</title>
</head>
<body>
<p id="number">0</p>
<button id="addBtn">点击按钮</button>
<script>
let addBtn = document.getElementById('addBtn');
let number = document.getElementById('number');
// 接收者
let worker = {
add() {
number.innerHTML = parseInt(number.innerHTML) + 1;
}
}
// 命令
class AddCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.add();
}
}
let addCommand = new AddCommand(worker);
// 调用者
addBtn.onclick = () => addCommand.execute();
</script>
</body>
</html>
撤销和重做
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>弹出菜单</title>
</head>
<body>
<p id="number">0</p>
<button id="addBtn">add</button>
<button id="undoBtn">undo</button>
<script>
let addBtn = document.getElementById('addBtn');
let undoBtn = document.getElementById('undoBtn');
let number = document.getElementById('number');
let worker = {
lastVal: -1,
add() {
let oldVal = parseInt(number.innerHTML);
worker.lastVal = oldVal;
number.innerHTML = oldVal + 1;
},
undo() {
number.innerHTML = worker.lastVal;
}
}
class AddCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.add();
}
}
let addCommand = new AddCommand(worker);
class UndoCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.undo();
}
}
let undoCommand = new UndoCommand(worker);
addBtn.onclick = () => addCommand.execute();
undoBtn.onclick = () => undoCommand.execute();
</script>
</body>
</html>
多步撤销和重做
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>弹出菜单</title>
</head>
<body>
<p id="number">0</p>
<button id="addBtn">add</button>
<button id="undoBtn">undo</button>
<button id="redoBtn">redo</button>
<script>
let addBtn = document.getElementById('addBtn');
let undoBtn = document.getElementById('undoBtn');
let redoBtn = document.getElementById('redoBtn');
let number = document.getElementById('number');
let worker = {
history: [],
index: -1,
add() {
let oldVal = parseInt(number.innerHTML);
let newVal = oldVal + 1;
worker.history.push(newVal);
worker.index = worker.history.length - 1;
number.innerHTML = newVal;
console.log(worker);
},
undo() {
if (worker.index - 1 >= 0) {
worker.index--;
number.innerHTML = worker.history[worker.index];
console.log(worker);
}
},
redo() {
if (worker.index + 1 < worker.history.length) {
worker.index++;
number.innerHTML = worker.history[worker.index];
console.log(worker);
}
}
}
class AddCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.add();
}
}
let addCommand = new AddCommand(worker);
class UndoCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.undo();
}
}
let undoCommand = new UndoCommand(worker);
class RedoCommand {
constructor(receiver) {
this.receiver = receiver;
}
execute() {
this.receiver.redo();
}
}
let redoCommand = new RedoCommand(worker);
addBtn.onclick = () => addCommand.execute();
undoBtn.onclick = () => undoCommand.execute();
redoBtn.onclick = () => redoCommand.execute();
</script>
</body>
</html>
打赏作者
您的打赏是我前进的动力
微信
支付宝
组合模式
上一篇
评论