摘要:要注意這里的一個狀態(tài)行為因為這個詞是狀態(tài)模式中最重要的個概念??紤]到這點,聰明的在中推出了狀態(tài)機這個偽函數(shù),能夠幫助我們快速實現(xiàn)狀態(tài)化。這里就引入了狀態(tài)機這個概念,以及和他對應(yīng)的狀態(tài)表。
?首先聲明一點,這個模式是我目前見過最好用(本人觀點),但是也是最難理解的一個(本人觀點)。 所以大家需要做好心理準備,如果,對這個模式?jīng)]有特別強烈的需求,比如: 我有一個Button,我按次數(shù)點擊它,他會觸發(fā)不同的狀態(tài) 等等這樣的,可以學習一下其他的模式。但是!!! 如果你看了我這篇文章,被我前面說的話嚇到了,那么就繼續(xù)往下看,其實,狀態(tài)模式是最好用,也是最容易掌握的一個。
大話狀態(tài)模式上面已經(jīng)提到了,狀態(tài)模式其實就是,一個事物的內(nèi)部狀態(tài)的改變,產(chǎn)生不同的行為。 要注意這里的
"一個","狀態(tài)","行為". 因為這3個詞是狀態(tài)模式中最重要的3個概念。
同樣,舉個栗子
大家家里都有空調(diào)吧,remoter應(yīng)該都用過。 首先我們拿一個簡單的使喚。 就拿 on/off 鍵吧。首先,遙控器會記錄當前的狀態(tài)(假設(shè)他會這樣),如果是on, 當你點擊 他會發(fā)出off的信號,如果你是off,則會發(fā)出on的信號.我們用程序說明一下.
var switches = (function(){ var state = "off"; return function(){ if(state === "off"){ console.log("打開空調(diào)"); state = "on"; }else if(state === "on"){ console.log("關(guān)閉空調(diào)"); state = "off"; } } })(); document.querySelector(".switch").addEventListener("click",function(){ switches(); //模仿你打開/關(guān)閉空調(diào)的狀態(tài) },false)
很簡單吧,一個閉包+一個變量 就可以構(gòu)成一個 狀態(tài)機,是不是超級神奇呢?
恩,看到這里,聰明的人會,會心一笑,然后繼續(xù)往下看。
是個屁。
想想,如果空調(diào)開關(guān)要是都只有兩種狀態(tài),尼瑪誰用啊!!!
滿是turn on/off. 就跟寫2進制一樣。實話說,哥匯編學的差,所以也十分不愿意直視二進制,你讓我使喚開關(guān)跟寫匯編似的,操。 0001是開,0000是關(guān),0010是加熱模式,0011是制冷模式。
所以,海爾,美的考慮到國情,制造了比較人性化的remoter。
現(xiàn)在,如果我們使用上面那種模式來寫,切換模式的switch。
var switches = (function(){ //auto->hot->cold->wind->dry->auto var state = "auto"; return function(){ if(state === "auto"){ console.log("制熱"); state = "hot"; }else if(state === "hot"){ console.log("制冷"); state = "cold"; }else if(state === "cold"){ console.log("送風"); state = "wind"; }else if(state === "wind"){ console.log("除濕"); state = "dry"; }else if(state === "dry"){ console.log("自動"); state = "auto"; } } })(); document.querySelector(".switch").addEventListener("click",function(){ switches(); //模仿你切換空調(diào)的模式 },false);
呵呵呵~ 功能是實現(xiàn)了,不過代碼,又被if語句給rape的。 性能的強奸犯,閱讀的殺手 恐怕就算if語句了. 所以,為了不犯罪,我們需要優(yōu)化我們的狀態(tài)模式。
高級狀態(tài)模式其實,這個狀態(tài)模式的寫法和命令模式有著異曲同工的妙處。即,中間有個狀態(tài)倉庫,然后分別將命令轉(zhuǎn)發(fā)給對應(yīng)的執(zhí)行類。
總結(jié)一下。 高級狀態(tài)模式需要有,狀態(tài)倉庫,狀態(tài)類,狀態(tài)執(zhí)行者,這3個要點。 對應(yīng)著我們的,”一個“,"狀態(tài)","行為". 一個倉庫,不同的狀態(tài),不同的執(zhí)行。
just do it.
//定義狀態(tài) var Auto= function(button){ this.turn = button; } Auto.prototype.press= function(){ console.log("制熱"); this.turn.setState("hot"); } var Hot = function(button){ this.turn = button; } Hot.prototype.press= function(){ console.log("制冷"); this.turn.setState("cold"); } var Cold = function(button){ this.turn = button; } Cold.prototype.press= function(){ console.log("送風"); this.turn.setState("wind"); } var Wind = function(button){ this.turn = button; } Wind.prototype.press= function(){ console.log("除濕"); this.turn.setState("dry"); } var Dry = function(button){ this.turn = button; } Dry.prototype.press= function(){ console.log("自動"); this.turn.setState("auto"); } //定義狀態(tài)倉庫 var Remoter = function(){ this.auto = new Auto(this); this.hot = new Hot(this); this.cold = new Cold(this); this.wind = new Wind(this); this.dry = new Dry(this); this.state = "auto"; } Remoter.prototype.setState = function(state){ this.state=state; } Remoter.prototype.press = function(){ this[this.state].press(); //執(zhí)行對應(yīng)狀態(tài)的press } Remoter.prototype.init = function(){ //定義執(zhí)行者 document.querySelector(".switch").addEventListener("click",()=>{ this.press(); },false); } new Remoter.init(); //初始化
上面那種就是一個比較模式化的寫法,而且,可復用,可添加。 比上面那種的逼格不知道高到哪里去,但是,實現(xiàn)成本也是挺大的??紤]到這點,聰明的ECMA-262在es6中推出了狀態(tài)機這個偽函數(shù),能夠幫助我們快速實現(xiàn)狀態(tài)化。
Duang~
就是generator函數(shù)。 目前FF,edge,chrome 最新版本已經(jīng)支持。不過可以使用babel進行轉(zhuǎn)化.
我們使用generator進行重構(gòu).
我比較懶,我們就先實現(xiàn)前3個模式的轉(zhuǎn)化吧。
var auto = function(){ console.log("自動"); } var hot = function(){ console.log("制熱"); } var cold = function(){ console.log("制冷"); } function* models(){ for(var i = 0,fn,len=arguments.length;fn = arguments[i++];){ yield fn(); if(i===len){ i = 0; } } } var exe = models(auto,hot,cold); //按照模式順序排放 document.querySelector(".switch").addEventListener("click",function(){ exe.next(); },false);
已經(jīng)沒有了if來進行分支判斷,效果也是蠻不錯的。 關(guān)于generator的用法,還有進程控制,這些都是比較高級的用法,有興趣的同學可以參考 阮老師的 es6講解. 但是,推薦還是使用,一個倉庫,不同狀態(tài),不同行為,這樣函數(shù)對象式的寫法,擴展性比較強。主要原因是因為,generator還未普及,以及設(shè)置他進程的順序比較復雜。不過,平常本人喜歡裝裝逼,永遠熱愛新技術(shù),所以大部分時候還是會使用generator。 總之,程序員并不是程序員,我們要有自己的核心價值觀,找到自己最對的 "瑪卡瑞納",這才是我們程序員應(yīng)該有的情懷。
我們仔細觀察一下上面使用"類"寫出來的狀態(tài)模式,會發(fā)現(xiàn),狀態(tài)類是不是感覺可以使用享元模式優(yōu)化呢?沒錯。因為他的方法和狀態(tài)都是一致的,當然可以使用。
var obj = { auto(){ console.log("自動") return "hot"; }, hot(){ console.log("制熱"); return "cold"; }, cold(){ console.log("制冷"); return "auto" } } var State = function(){ this.state = "auto"; this.obj = obj; } State.prototype.next = function(){ this.state = this.obj[this.state]() } new State().next(); //測試通過.
這只是一種比較輕巧的方法,js,最出名的就是他的動態(tài),無拘無束,你可以天馬行空的寫出你的代碼(但是,必須保證的你代碼不會變成 鳳姐 ).
但從上面的代碼可以看出,如果程序里面使用return 的話,很容易會造成你函數(shù)的邏輯復雜度,所以我們這里推薦使用一個state進行保存,將this.state傳入。 當然,我們并不是當參數(shù)參入了(太low),我們使用委托的技術(shù)傳入,相當于給this動態(tài)織入一個函數(shù)。這個方法就叫: apply和call. 哈哈,是不是有種感覺(怎么又是你).
var obj = { auto(){ console.log("自動") this.state = "hot"; }, hot(){ console.log("制熱"); this.state = "cold"; }, cold(){ console.log("制冷"); this.state = "auto"; } } var State = function(){ this.state = "auto"; this.obj = obj; } State.prototype.next = function(){ this.obj[this.state].call(this); } new State().next();
沒錯,這下,我們不僅能將函數(shù)動態(tài)織入,而且可以直接改動state,這樣可以給自己程序的擴展性加上一分。
當然,狀態(tài)模式的寫法還有很多,比如delegate函數(shù)的寫法等等。 不過,找到自己的"瑪卡瑞納"才是最棒的。
上面只是一個線上的流式狀態(tài)切換,并沒有涉及很復雜的業(yè)務(wù)邏輯。但是,如果你在開發(fā)一個大型項目的時候,涉及的狀態(tài)可謂是五花八門,還是以空調(diào)遙控器為例,比如,你切換到模式選擇的時候,你的上下左右鍵,只能控制模式的切換,而不能控制風速大小,當你切換到風速選擇模式的時候,同樣不能控制其他的功能。 所以,如果按照上面那種 單線式的狀態(tài)切換是不夠的。 這里就引入了FsM(finite-state-machine),狀態(tài)機這個概念,以及和他對應(yīng)的狀態(tài)表。
如下圖
如果你是學機械的,那么這個狀態(tài)切換的概念應(yīng)該非常熟悉,在CH40161(一種自觸發(fā)式芯片)中,你輸入一個觸發(fā)信號,他可以按照你這個觸發(fā)信號逐步觸發(fā)(我機械太渣,但意外的喜歡上計院). 在js中,gordon大神(有8個contributor)已經(jīng)寫出了這個狀態(tài)庫。有興趣的同學可以看一看。
傳送門: FSM。
其實,他里面最重要的就是"狀態(tài)"和"狀態(tài)切換"的規(guī)則。
先看一個demo:
var fsm = StateMachine.create({ initial: "green", events: [ { name: "warn", from: "green", to: "yellow" }, { name: "panic", from: "yellow", to: "red" }, { name: "calm", from: "red", to: "yellow" }, { name: "clear", from: "yellow", to: "green" } ], callbacks: { onpanic: function(event, from, to, msg) { alert("panic! " + msg); }, onclear: function(event, from, to, msg) { alert("thanks to " + msg); }, ongreen: function(event, from, to) { document.body.className = "green"; }, onyellow: function(event, from, to) { document.body.className = "yellow"; }, onred: function(event, from, to) { document.body.className = "red"; }, } });
這已經(jīng)定義好了一個完整的單線式,狀態(tài)切換隊列。
當你觸發(fā)fsm.warn(); 狀態(tài)就是從green->yellow。
當你觸發(fā)fsm.panic(); 狀態(tài)就是從yellow->red.
...
說一下基本用法
events 里面就是你定義的狀態(tài)表的規(guī)則
name: 標識,狀態(tài)切換的函數(shù)名 from: 標識 為切換之前的狀態(tài) to: 標識 為切換之后的狀態(tài)
callbacks 里面就是對狀態(tài)和切換規(guī)則函數(shù)的定義. 這里不說的太復雜,就按照基本的講解吧。
使用on+Name; 定義狀態(tài)切換的函數(shù) 使用on+State: 定義某個狀態(tài)時觸發(fā)的函數(shù)
當然,還有
onbeforeevent - fired before any event onleavestate - fired when leaving any state onenterstate - fired when entering any state onafterevent - fired after any event
這些比較細,這里就不做詳細介紹,如果有興趣的同學可以去github上面看一看,理解起來也不是很難。我這里介紹的我經(jīng)常使用的。
所以,上面的流程就是。
使用fsm.panic() 之后。
觸發(fā)順序為: onpanic()->red();
如果你狀態(tài)不對,而強行調(diào)用fsm.panic的話就會觸發(fā)error函數(shù)(這里沒有寫). 所以,上面寫的fsm 差不多已經(jīng)夠用了,關(guān)鍵看你如果組合了。 要知道,二維難度 >> 一維難度。 有一個好工具,能把你的工作量降到最低。
說到這里,我的這篇blog大部分是介紹 一些基本原理和方法,狀態(tài)模式的應(yīng)用在程序設(shè)計中是非常重要的一個概念,如果你掌握了,語言只會變?yōu)槟愕囊粋€工具,因為 你已經(jīng)吃透了 隱藏在 語言背后的 secret. 最后還是那句話, 不要為了模式而模式,但狀態(tài)模式確實是個好模式。
ending~.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/78547.html
摘要:什么是狀態(tài)模式狀態(tài)模式對象行為是基于狀態(tài)來改變的。原文地址設(shè)計模式手冊之狀態(tài)模式優(yōu)缺點優(yōu)點封裝了轉(zhuǎn)化規(guī)則,對于大量分支語句,可以考慮使用狀態(tài)類進一步封裝。 1. 什么是狀態(tài)模式? 狀態(tài)模式:對象行為是基于狀態(tài)來改變的。 內(nèi)部的狀態(tài)轉(zhuǎn)化,導致了行為表現(xiàn)形式不同。所以,用戶在外面看起來,好像是修改了行為。 Webpack4系列教程(17篇) + 設(shè)計模式手冊(16篇):GitHub地址 博...
摘要:什么是狀態(tài)模式狀態(tài)模式對象行為是基于狀態(tài)來改變的。原文地址設(shè)計模式手冊之狀態(tài)模式優(yōu)缺點優(yōu)點封裝了轉(zhuǎn)化規(guī)則,對于大量分支語句,可以考慮使用狀態(tài)類進一步封裝。 1. 什么是狀態(tài)模式? 狀態(tài)模式:對象行為是基于狀態(tài)來改變的。 內(nèi)部的狀態(tài)轉(zhuǎn)化,導致了行為表現(xiàn)形式不同。所以,用戶在外面看起來,好像是修改了行為。 Webpack4系列教程(17篇) + 設(shè)計模式手冊(16篇):GitHub地址 博...
摘要:集成到去使用如果想在中使用,想到比較方便的使用形式是高階組件,需要用到有限狀態(tài)機的組件傳進高階組件,就立馬擁有了使用有限狀態(tài)機的能力。 背景 近年來由于一些前端框架的興起而后逐漸成熟,組件化的概念已經(jīng)深入人心,為了管理好大型應(yīng)用中錯綜復雜的組件,又有了單向數(shù)據(jù)流的思想指引著我們,Vuex、Redux、MobX等狀態(tài)管理工具也許大家都信手拈來。我們手握著這些工具,不斷思考著哪些數(shù)據(jù)應(yīng)該放...
摘要:如果非要重寫父類的方法,比較通用的做法是原來的父類和子類都繼承一個更通俗的基類,原有的繼承關(guān)系去掉,采用依賴聚合,組合等關(guān)系代替。里氏替換原則通俗的來講就是子類可以擴展父類的功能,但不能改變父類原有的功能。一有限狀態(tài)機狀態(tài)總數(shù)是有限的。 設(shè)計模式 抽象類 抽象類的表現(xiàn) 不能被實例,只能被繼承 最少有一個抽象方法(多態(tài)的具體體現(xiàn)) // 汽車抽象類,當使用其實例對象的方法時會拋出錯誤...
摘要:原文鏈接本文內(nèi)容包含以下章節(jié)本書英文版這個章節(jié)主要討論了在游戲中經(jīng)常用到的一些基礎(chǔ)的人工智能算法。行為樹是把的圖轉(zhuǎn)變成為一顆樹結(jié)構(gòu)。根據(jù)當前游戲的環(huán)境狀態(tài)得到某一個行為的效用值。 作者:蘇博覽商業(yè)轉(zhuǎn)載請聯(lián)系騰訊WeTest獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。原文鏈接:https://wetest.qq.com/lab/view/427.html 本文內(nèi)容包含以下章節(jié): Chapter 2 ...
閱讀 1368·2023-04-26 02:42
閱讀 1695·2021-11-12 10:36
閱讀 1903·2021-10-25 09:47
閱讀 1338·2021-08-18 10:22
閱讀 1869·2019-08-30 15:52
閱讀 1281·2019-08-30 10:54
閱讀 2696·2019-08-29 18:46
閱讀 3557·2019-08-26 18:27