摘要:而且,說完起立之后,副班長也不知道誰會說老濕好。不要緊,我們可以使用命令模式來彌補這個缺陷,因為命令模式最大的一個擴展性就是命令者和命令的執行者分開了。這樣可以清楚的說明命令模式的優勢到底在哪里。
命令模式
望文生義,所謂的命令模式其實就是:
發出一定的指令,然后由對象接受并且執行
需要強調一點,就是對于命令的發出者來說,他并不知道該命令是給誰,執行效果是怎樣,他只管發出命令就行。聽到這里感覺和發布訂閱者模式有異曲同工的效果。 但事實上,他們兩者應用的場景還是有比較大的區分。不僅寫法上有不同,而且執行的過程也有所不同。要知道在命令模式里面執行的效果是1對1,而在訂閱者模式里面是1對>=1的.
你在逼逼什么?
哦,說明白一點。 就和上課一樣。 老師進教室了,首先說:“上課!”. 接著,你們的monitor 會立馬接上: "起立!"。 然后,你們就會異口同聲的說:"老濕好~"。 沒錯,分析一下。 當老師說上課的時候,他并不會知道誰會說起立,比如今天班長談戀愛去了,那副班長頂上。 而且,說完起立之后,副班長也不知道誰會說老濕好。 也就是命令的發出者,只管發出一個命令,然后你們只管執行就over了.
talk is cheap, show u code.
//事件發出者 var setCommand = function(ele,command){ //命令的綁定者 ele.onclick = function(){ command.do(); } } //事件的執行者 var location = (function() { //執行事件類 var ball = getEle("#ball"); var move = function(direct) { return function() { var style = ball.style, dir = parseInt(style[direct]); style[direct] = `${dir-200}px`; } } var moveUp = move("top"); var moveDown = move("down"); var moveLeft = move("left"); var moveRight = move("right"); return { moveUp, moveDown, moveLeft, moveRight } })(); //封裝命令 var MoveUp = function(exer){ this.exer = exer; } MoveUp.prototype.do = function(){ this.exer.moveUP(); } var MoveDown = function(exer){ this.exer = exer; } MoveDown.prototype.do = function(){ this.exer.moveDown(); } var MoveLeft = function(exer){ this.exer = exer; } MoveLeft.prototype.do = function(){ this.exer.moveLeft(); } var MoveRight = function(exer){ this.exer = exer; } MoveRight.prototype.do = function(){ this.exer.moveRight(); } setCommand(getEle("upBtn"), new MoveUp(location)); //給向上的button,綁定向上的執行程序 setCommand(getEle("downBtn"), new MoveDown(location)); //... setCommand(getEle("leftBtn"), new MoveLeft(location)); //... setCommand(getEle("rightBtn"), new MoveRight(location)); //....
可以清晰的看到,在命令模式中,觸發事件(onclick)和執行程序(command.do())都是已知的。 但是這個執行的消息給誰,或者執行產生的效果是怎樣的,在命令的發出者這一方都是未知的。需要注意的是,這時候的未知只限于命令的發出者而言。也就是現在命令模式將發出者和執行者給解耦開,即,可變的部分和不可變的部分分開。
上面逼逼這么多到底在說shenme...
其實一切原理都是枯燥的,實例才是王道。 來,我們來做個比較。也就是不使用命令模式,直接寫上面的例子(偷個懶,只寫moveUP部分).
var ele = getEle("#ball"); getELe(".moveUp").onclick = function(){ var style = this.style, dir = parseInt(style["top"]); style["top"] = `${dir-200}px`; } }
上面的代碼同樣能完成上面辣么辣么長的代碼完成的效果,那為什么還要使用上面的寫法呢?
艸~ 請問,你下面那段代碼,能體現你的bigger嗎? 能體現你是代碼藝術家的feeling嗎?能體現你的思維能力嗎?
No NO No~
我們來分析下why.
首先下面那段代碼可以完成上面的功能,但是萬一有一天,一個名叫產經的生物和你說
"親愛的,你能不能在加一個button,讓這個球可以斜著走,可以轉個圈呢? 哈哈,我相信你一定可以的。"
呵呵,你話都沒說。 想當然這個鍋,你必須背。好吧,那開始做吧。(用那個渣渣代碼演示一遍).
function getY(x){ var k = 1.2; return k*x; } getELe("#diagnoal").onclick = function(){ var style = ele.style, x = parseInt(style["left"]), y = parseInt(style["top"]), style["left"] = `${x-200}px`; style["top"] = `${y-getY(200)}px`; } }
可以想象,最后如果產經的需求不斷增多,那么你在事件處理的回調會越來越復雜,比如:
"親愛的,你斜著走都實現了,那4個方向能不能都可以走呢?"
我想這時候,你應該會懵逼了。不要緊,我們可以使用命令模式來彌補這個缺陷,因為命令模式最大的一個擴展性就是命令者和命令的執行者分開了。而且在上面面向過程的代碼中,看不出什么邏輯出來,只是知道,這個click是觸發什么的。 而事件回調中的代碼重用性也是非常低的。
這里使用命令模式重構一遍
//其他的還是一樣,這里主要將4個方向的代碼重構一下
var location = (function() { //執行事件類 var ball = getEle("#ball"); var compMove = function(hori,vert) { //垂直和水平方向 var k = 1.2; //移動的斜率 var getY = function() { return k * x; } return function() { var style = ball.style, x = parseInt(style[hori]), //水平方向上的位置 y = parseInt(style[vert]); //垂直方向上的位置 style[hori] = `${x-200}px`; style[vert] = `${y-getY(200)}px`; //執行移動 } }; //斜方向綁定代碼 var moveLU = compMOve("left","top"); var moveRU = compMOve("right","top"); var moveLB = compMOve("left","bottom"); var moveRB = compMOve("right","bottom"); return { moveLU moveRU, moveLB, moveRB } })(); //封裝命令 var MoveLU = function(exer) { this.exer = exer; } MoveLU.prototype.do = function() { this.exer.moveLU(); } setCommand(getEle("leftUpBtn"), new MoveLU(location)); //給向上的button,綁定向上的執行程序
可以看出來,雖然代碼多,但是至少我們將改動的地方降到最低了。
setCommand這個不變,變的只是綁定click的對象和執行者。 這樣可以清楚的說明命令模式的優勢到底在哪里。
當然,我們還可以做一個優化,要知道,js是一門函數至上的語言,因為函數可以像參數一樣被傳來傳去,所以可以這樣改寫命令的綁定者.
var setCommand = function(ele,fn){ ele.onclick = function(){ fn(); } } setCommand(getEle("leftUpBtn",()=>{location.moveLU()}))); //給向上的button,綁定向上的執行程序
這樣就可以省去中間一大堆的事件修飾,從而將函數直接暴露使用。推薦這樣寫法,因為這個才是js的真正實力。
要知道一個模式的精華不是看他能怎么用,而是要看你怎么用他。
其實,緩存并不是什么高上大的東西,就是在函數里名,有一個變量來保存你的結果,而你可以遍歷這個結果.
function fb(num) { if (num <= 1) { return 1; } return num * fb(--num) } //緩存代理出場了 var cProxy = (function() { var cache = {}; return function(num) { if (cache[num]) { console.log(`this is cache ${cache[num]}`); return cache[num]; } return cache[num] = fb(num); } })(); //測試 console.log(cProxy(4)); //24 cProxy(4); //"this is cache 24"
上面是我以前寫代理緩存的例子。 里面有個叫cache的東西,就是來保存你的結果(放在內存中),以備下次使用。
而命令模式的緩存有個極大的用途就是一個 撤銷和重做的效果.
在上面的例子中可以保留每一個節點小球的位置(簡單起見,還是以最初的上下左右為基準吧)
由于代碼過長,我放在fiddle里面(里面代碼可能會和上面有很大出入,但是如果理解了上面的說法的話,我相信理解起來肯定很快的)。有興趣可以看看。是個實例demo哦。 :)
撤銷實例
特此說明,由于使用原生寫的里面會有寫hacks,如果大家有自己獨到的見解,歡迎拍磚(請輕點~). 也歡迎點個推薦唄。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/78506.html
摘要:今天就先給大家講大數據分析工程師。所以你要和我說你在外面培訓了個月的大數據分析后,就想做了,那請你再考慮考慮。而他們鼓吹的大數據分析師,其實就是。 showImg(https://segmentfault.com/img/remote/1460000018722710?w=1000&h=750); Overview: 序 基本概念 DS的職能要求 DE的職能要求 總結 一、序 今天...
摘要:經驗少的程序員小猿同學畢業工作一年了,在公司感覺自己的能力很好了,能力大于老板給的價值了,所以想要漲工資,但是老板給漲的不夠理想,小猿聽說跳槽可以讓自己的工資翻倍,毅然決然的就辭職了,決定重新找工作。 又到了一周一次的周末心靈雞湯的時間了,希望大家能夠痛痛快快的喝了這碗雞湯,讓這酸爽的感覺使你永生難忘。哈哈……這周又有幾個人,尤其是畢業生在「非著名程序員」微信公眾號里私聊我關于找不到工...
摘要:我自己總結的學習的系統知識點以及面試問題,已經開源,目前已經。面試官那你都了解里面的哪些東西呢我哈哈哈這可是我的強項,從,說到,,又說到線程池,分別說了底層實現和項目中的應用。 我自己總結的Java學習的系統知識點以及面試問題,已經開源,目前已經 35k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://github.com/Snailclimb... ...
閱讀 1202·2021-11-23 09:51
閱讀 1980·2021-10-08 10:05
閱讀 2339·2019-08-30 15:56
閱讀 1900·2019-08-30 15:55
閱讀 2640·2019-08-30 15:55
閱讀 2487·2019-08-30 13:53
閱讀 3498·2019-08-30 12:52
閱讀 1250·2019-08-29 10:57