命令模式(Command)
Nov 01, 2022 ·
5 Min Read
命令模式指的是一个执行某些特定事情的指令。
个人理解:命令模式的代码很优雅,它不关心如何实现,实现的是谁,松开两个对象之间的耦合关系。即请求对象发出一条命令,给一个对象,对象帮你找请求的接受者。比如你要点菜,找服务员,你不需要知道谁帮你炒,你也不知道哪位厨师,你只需要向服务员下单即可,服务员去帮你找合适的厨师接收你的请求。
命令模式和策略模式其实早已经融于到 Javascript
这门语言中,只是使用的时候你并不知道这属于命令模式。
常用的场景:
- 需要向某个对象发送请求,但不知道请求的接受者是谁,也不知道被请求的操作是什么。用于消除耦合。
现在比如一个程序,按钮的绘制由某一个程序员来干,而另一个程序员负责编写点击按钮后的行为。
分析:对绘制的程序员来说,他完全不知道按钮未来会干什么,只知道会发生事情,那怎么给它绑定 onclick
事件呢?因此点击了按钮之后,必须向某些负责具体行为的对象发送请求,这些对象就是请求的接收者。
<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
函数可以自由传递,可简单多了。
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)
命令模式还要一个好处就是可以轻易实现撤销、重播等功能。
例:实现重播。按上下左右键,按重播的时候,重新执行你之前的操作。常用地方:比如象棋的悔棋。
<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>
延伸
宏命令
宏命令是命令模式与组合模式的联用产物。
打个比方:现在的智能家居,我设置了电影模式,当我发出这一命令时,它会自动把窗帘拉上,灯光调暗,打开投影。
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()
Last edited Feb 15