命令模式(Command)
命令模式指的是一个执行某些特定事情的指令。
个人理解:命令模式的代码很优雅,它不关心如何实现,实现的是谁,松开两个对象之间的耦合关系。即请求对象发出一条命令,给一个对象,对象帮你找请求的接受者。比如你要点菜,找服务员,你不需要知道谁帮你炒,你也不知道哪位厨师,你只需要向服务员下单即可,服务员去帮你找合适的厨师接收你的请求。
命令模式和策略模式其实早已经融于到 Javascript
这门语言中,只是使用的时候你并不知道这属于命令模式。
常用的场景:
- 需要向某个对象发送请求,但不知道请求的接受者是谁,也不知道被请求的操作是什么。用于消除耦合。
现在比如一个程序,按钮的绘制由某一个程序员来干,而另一个程序员负责编写点击按钮后的行为。
分析:对绘制的程序员来说,他完全不知道按钮未来会干什么,只知道会发生事情,那怎么给它绑定 onclick
事件呢?因此点击了按钮之后,必须向某些负责具体行为的对象发送请求,这些对象就是请求的接收者。
html
<button id="btn1">点击按钮1</button>
<button id="btn2">点击按钮2</button>
<button id="btn3">点击按钮3</button>
<script>
const btn1 = document.getElementById('btn1')
const btn2 = document.getElementById('btn2')
const btn3 = document.getElementById('btn3')
const setCommand = function (btn, command) {
btn.onclick = function () {
command.execute()
}
}
const MenuBar = {
refresh: () => {
console.log('刷新菜单目录')
}
}
const SubMenu = {
add: () => {
console.log('增加子菜单')
},
del: () => {
console.log('删除子菜单')
}
}
const RefreshMenuBarCommand = function (receiver) {
this.receiver = receiver
}
RefreshMenuBarCommand.prototype.execute = function () {
this.receiver.refresh()
}
const AddSubMenuCommand = function (receiver) {
this.receiver = receiver
}
AddSubMenuCommand.prototype.execute = function () {
this.receiver.add()
}
const DelSubMenuCommand = function (receiver) {
this.receiver = receiver
}
DelSubMenuCommand.prototype.execute = function () {
this.receiver.del()
}
const refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar)
const addSubMenuCommand = new AddSubMenuCommand(SubMenu)
const delSubMenuCommand = new DelSubMenuCommand(SubMenu)
// 设置命令
setCommand(btn1, refreshMenuBarCommand)
setCommand(btn2, addSubMenuCommand)
setCommand(btn3, delSubMenuCommand)
</script>
看了之后是不是感觉好麻烦,当然,这是面向对象语言的实现方法。在 Javascript
函数可以自由传递,可简单多了。
javascript
const MenuBar = {
refresh() {
console.log('刷新')
}
}
const RefreshMenuBarCommand = function(receiver) {
return {
execute: function() {
receiver.refresh()
}
}
}
const setCommand = function(button, command) {
button.onclick = function() {
command.execute()
}
}
const refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar)
setCommand(button1, refreshMenuBarCommand)
命令模式还要一个好处就是可以轻易实现撤销、重播等功能。
例:实现重播。按上下左右键,按重播的时候,重新执行你之前的操作。常用地方:比如象棋的悔棋。
html
<button id="replay">播放录像</button>
<script>
const Ryu = {
attack: function () {
console.log('攻击')
},
defense: function () {
console.log('防御')
},
jump: function () {
console.log('跳跃')
},
crouch: function () {
console.log('蹲下')
}
}
const makeCommand = function (receiver, state) {
return function () {
receiver[state]()
}
}
const commands = {
119: 'jump',
115: 'crouch',
97: 'defense',
100: 'attack'
}
const commandStack = []
document.onkeypress = function (ev) {
const keyCode = ev.keyCode;
const command = makeCommand(Ryu, commands[keyCode])
if (command) {
command()
commandStack.push(command)
}
}
document.getElementById('replay').onclick = function () {
let command
while (command = commandStack.shift()) {
command()
}
}
</script>
延伸
宏命令
宏命令是命令模式与组合模式的联用产物。
打个比方:现在的智能家居,我设置了电影模式,当我发出这一命令时,它会自动把窗帘拉上,灯光调暗,打开投影。
javascript
const closeCurtainCommand = {
execute: function() {
console.log('窗帘拉上')
}
}
const closeLightsCommand = {
execute: function() {
console.log('调暗灯光')
}
}
const openProjectorCommand = {
execute: function() {
console.log('打开投影')
}
}
const MacroCommand = function() {
return {
commandsList: [],
add: function(command) {
this.commandsList.push(command)
},
execute: function() {
for(let i = 0, command; command = this.commandsList[i++];) {
command.execute()
}
}
}
}
const macroCommand = MacroCommand()
macroCommand.add(closeCurtainCommand)
macroCommand.add(closeLightsCommand)
macroCommand.add(openProjectorCommand)
macroCommand.execute()