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