javascript設(shè)計模式之命令模式
一. 認(rèn)識命令模式
所謂命令,也就是指執(zhí)行某些特定事情的指令,就拿喝水的例子來說,喝水執(zhí)行的指令就是將水倒在杯子里,然后端起杯子送入口中,這就是一條命令,無論誰喝水都是這個步驟,我們不關(guān)心是誰端起了杯子,也不關(guān)心杯子中的水到底是水還是其他東西。我們只關(guān)心這個過程,將水倒在杯子中,然后端起杯子送入口中,最后誰喝掉了,喝掉的是飲料還是水不重要。
大家可能聽的有點混,而命令模式最常見的應(yīng)用場景就是:有時候需要向某些對象發(fā)送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是什么。此時我們用一種松耦合的方式來設(shè)計程序,使得請求發(fā)送者和接收者能夠消除彼此間的耦合關(guān)系。
二. 代碼實現(xiàn)-實際應(yīng)用場景
在實際開發(fā)中團(tuán)隊協(xié)作是最重要的環(huán)節(jié),假如兩個人分別得到兩個不同的任務(wù),一個人負(fù)責(zé)頁面中所有的 button 按鈕的美化,另一個人負(fù)責(zé)實現(xiàn)這些 button 的邏輯,我們在寫 button 邏輯時不知道該 button 最終綁定的是頁面中的哪一個按鈕,也不知道具體點擊該按鈕會發(fā)生什么,此時我們用命令模式來設(shè)計,來解開按鈕和負(fù)責(zé)具體行為對象間的耦合,代碼如下所示。
<script> let btns = document.querySelectorAll('button'); // 寫一個刷新命令 let refreshCommand = { execute () { console.log("刷新頁面"); } } // 寫一個登錄按鈕命令 let loginCommand = { execute () { console.log("執(zhí)行登錄操作"); } } let setCommand = function( button, command) { button.onclick = function() { command.execute(); } } // 假如第三個按鈕用來負(fù)責(zé)刷新頁面,第五個用來負(fù)責(zé)登錄操作 setCommand( btns[2], refreshCommand); setCommand( btns[4], loginCommand);
分析:我們將點擊按鈕后可能會發(fā)生的事件分別封裝起來,它們中各自有自己的 execute 函數(shù),我們將安裝按鈕功能的函數(shù)封裝到了 setCommand 中,最后根據(jù)需求對應(yīng)安裝各個按鈕的功能便可。
三. 命令模式的撤銷操作
下達(dá)一個命令后,我們可能不需要了,想回到下達(dá)命令之前的狀態(tài),就需要用到撤銷操作,撤銷操作的實現(xiàn)一般是給命令對象增加一個 undo 或者 unexecute 方法,在其中執(zhí)行 execute 的反向操作。
// 假如寫一個人物向左移的命令 let moveLeftCommand = { execute() { console.log("人物向左移動"); } undo (){ console.log("人物向又移動"); } }
這種是簡單的邏輯實現(xiàn),假如有些撤銷行為無法用執(zhí)行他的反向操作來實現(xiàn),那我們應(yīng)該怎么辦呢?
帶著這個問題我們學(xué)習(xí)一種新的達(dá)到撤銷的方式,我們用一個棧來記錄每一步命令,想要撤銷,我們只需要重頭開始,從棧中拿出命令來分別執(zhí)行,將最后一次命令刪除即可。如果不刪除最后一次命令依次執(zhí)行,我們又實現(xiàn)了一個新的功能,重播。
這是一個通過 WASD 來控制小球移動的代碼實現(xiàn),我們每次按下一個有效命令時,便會將該命令推入 commandStack 中來記錄小球的變化,當(dāng)點擊重播時小球會按照之前同樣的軌跡運(yùn)動。
let role = document.getElementById('role'); let move = { dom: role, leftt:0, top: 0, up() { console.log("向上"); this.top -= 10; this.dom.style.top = this.top + 'px'; }, down(){ console.log("向下"); this.top += 10; this.dom.style.top = this.top + 'px'; }, left() { console.log("向左"); this.leftt -= 10; this.dom.style.left = this.leftt + 'px'; }, right() { console.log("向右"); this.leftt += 10; this.dom.style.left = this.leftt + 'px'; } } commands = { "119": "up", // W "115": "down", // S "97": "left", // A "100": "right" // D }; commandStack = []; // 保存命令的堆棧 let setCommand = function (receiver, state) { return function() { receiver[state](); } } document.addEventListener('keypress', (e) => { let code = e.charCode; if(!commands[code]) { return ; } let command = setCommand(move, commands[code]); if( command ) { command(); commandStack.push( command ); } }) // 設(shè)置重播按鈕 document.getElementById('replay').onclick = function() { let command; while(command = commandStack.shift()) { command(); } }
四. 宏命令
宏命令就是一組命令的集合,通過執(zhí)行宏命令可以一次執(zhí)行一批命令。
通俗來說就是,有一件事你需要重復(fù)做,并且做這件事的每一步都是固定不變的,那么我們可以將做這件事情的所有步驟打包起來,只需要啟動,便可以自動完成所有步驟。
需求:實現(xiàn)一個游戲自動刷副本的腳本
? let MacroCommand = function() { return { commandList: [], add( command ) { this.commandList.push(command); }, execute () { for(let i = 0,command; command = this.commandList[i++];){ command.execute(); } } } } let startGameCommand = { execute() { console.log('開始游戲') } } let killGhostCommand = { execute() { console.log('打怪') } } let giftCommand = { execute() { console.log("領(lǐng)取獎勵") } } let outGameCommand = { execute() { console.log("退出游戲") } } let Game = MacroCommand(); Game.add(startGameCommand); Game.add(killGhostCommand); Game.add(giftCommand); Game.add(outGameCommand); Game.execute();
我們只需要為宏命令對列中添加命令,它會自動執(zhí)行里邊的所有命令。我們只負(fù)責(zé)啟動,其他的就不需要我們管了,我們可以放心的去做其他事情,等待它自己完成。
五. 總結(jié)
命令模式中也提到了傻瓜命令和智能命令,它兩的區(qū)別在于傻瓜命令中含有命令的接收者,而智能命令沒有,它直接提供請求。智能命令與策略模式十分相近,只是在使用上不同,策略模式中對象的目標(biāo)相同,只是實現(xiàn)目標(biāo)的算法不同,而命令模式的目標(biāo)更具有散發(fā)性。命令模式還可以完成撤銷、排隊的功能。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
JavaScript Math.ceil 方法(對數(shù)值向上取整)
js Math.ceil用于對數(shù)值向上取整,即得到大于或等于該數(shù)值的最小整數(shù),需要的朋友可以參考下2015-01-01javascript SpiderMonkey中的函數(shù)序列化如何進(jìn)行
JavaScript中如何進(jìn)行函數(shù)序列化,函數(shù)序列化的作用是什么?本文將介紹SpiderMonkey中的函數(shù)序列化,有需要的朋友可以參考下2012-12-12javascript replace()第二個參數(shù)為函數(shù)時的參數(shù)用法
replace()函數(shù)具有替換功能,它可以具有兩個參數(shù),第一個參數(shù)可以是要被替換的字符串或者匹配要被替換字符串的正則表達(dá)式,第二個參數(shù)可以是替換文本或者一個函數(shù),下面看一下關(guān)于replace()函數(shù)的幾個代碼實例2016-12-12JavaScript中用于生成隨機(jī)數(shù)的Math.random()方法
這篇文章主要介紹了JavaScript中用于生成隨機(jī)數(shù)的Math.random()方法,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-06-06