JS設(shè)計(jì)模式之狀態(tài)模式的用法使用方法
狀態(tài)模式(State Pattern)
一種行為型設(shè)計(jì)模式,核心是對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變其行為。
狀態(tài)模式將對(duì)象的行為封裝到不同的狀態(tài)類(lèi)中,使得對(duì)象在不同狀態(tài)下可以選擇不同的行為。
組成
環(huán)境類(lèi):環(huán)境類(lèi)是擁有狀態(tài)的對(duì)象,它在運(yùn)行時(shí)將自身的操作委托給當(dāng)前的狀態(tài)對(duì)象來(lái)處理。①環(huán)境類(lèi)維護(hù)一個(gè)對(duì)抽象狀態(tài)類(lèi)的引用,②并通過(guò)該引用來(lái)調(diào)用具體狀態(tài)類(lèi)的方法。
抽象狀態(tài)類(lèi):抽象狀態(tài)類(lèi)定義了一個(gè)接口③,用于封裝與環(huán)境類(lèi)的特定狀態(tài)相關(guān)的行為。
具體狀態(tài)類(lèi):具體狀態(tài)類(lèi)實(shí)現(xiàn)了抽象狀態(tài)類(lèi)定義的接口,它具體描述了在某個(gè)狀態(tài)下對(duì)象的行為④。
工作流程
環(huán)境類(lèi)通過(guò)持有一個(gè)抽象狀態(tài)類(lèi)的引用來(lái)表示當(dāng)前的狀態(tài)。
客戶(hù)端通過(guò)調(diào)用環(huán)境類(lèi)的方法來(lái)觸發(fā)某個(gè)操作。
環(huán)境類(lèi)在運(yùn)行時(shí)將操作委托給當(dāng)前狀態(tài)對(duì)象進(jìn)行處理。
當(dāng)對(duì)象的狀態(tài)發(fā)生改變時(shí),環(huán)境類(lèi)會(huì)更新當(dāng)前狀態(tài)對(duì)象的引用,從而改變對(duì)象的行為。
趣例
下面的這個(gè)例子展示了一位西安老鐵觀看國(guó)足比賽的時(shí)候的心理狀態(tài)變化過(guò)程:
abstract class State { ? constructor(public context: Context | null = null) {} ? // 這里體現(xiàn)出③:用于封裝與環(huán)境類(lèi)的特定狀態(tài)相關(guān)的行為 ? handle() { ? ? if (!this.context) { ? ? ? console.log("找不到老鐵了!"); ? ? } else { ? ? ? this.exec(); ? ? } ? } ? next<T extends State>(NextState: new (context: Context) => T) { ? ? if (!this.context) { ? ? ? console.log("找不到老鐵了!"); ? ? } else { ? ? ? this.context.to(new NextState(this.context)); ? ? } ? } ? abstract exec(): any; } // 具體狀態(tài)類(lèi) class StateA extends State { ? // 這里體現(xiàn)出④:用于封裝與環(huán)境類(lèi)的特定狀態(tài)相關(guān)的行為 ? exec() { ? ? console.log("買(mǎi)票"); ? ? this.next(StateB); ? } } class StateB extends State { ? exec() { ? ? console.log("坐車(chē)"); ? ? this.next(StateC); ? } } class StateC extends State { ? exec() { ? ? console.log("看球"); ? ? this.next(StateO); ? } } class StateO extends State { ? exec() { ? ? throw new Error('已經(jīng)結(jié)束了'); ? } } // 環(huán)境類(lèi) class Context { ? // 這里體現(xiàn)出①:環(huán)境類(lèi)維護(hù)一個(gè)對(duì)抽象狀態(tài)類(lèi)的引用,即cts ? constructor(public cts: State | null = null) {} ? to(state: State) { ? ? console.log(`Transitioning to ${state.constructor.name}`); ? ? this.cts = state; ? } ? do() { ? ? // 這里體現(xiàn)出②:通過(guò)該引用來(lái)調(diào)用具體狀態(tài)類(lèi)的方法 ? ? this.cts?.handle(); ? } } // 使用示例 const context = new Context(); const stateA = new StateA(context); context.to(stateA); while(1){ ? try { ? ? context.do(); ? } catch { ? ? console.log('肯定輸了!'); ? ? break; ? } }
對(duì)照例子理解
- 狀態(tài)模式:一種行為型設(shè)計(jì)模式,核心是①【對(duì)象】在其②【內(nèi)部狀態(tài)】③【改變】時(shí)④【改變其行為】。
①對(duì)象:指的是環(huán)境類(lèi)對(duì)象,也就是上述代碼中的context對(duì)象。
②內(nèi)部狀態(tài),指的是context引用的狀態(tài)類(lèi)實(shí)例,也就是stateA以及NextState的匿名實(shí)例。
③改變:指的是context.to的調(diào)用。
④行為:指的是context.do函數(shù)的執(zhí)行結(jié)果。
改變其行為:指的是context.cts引用不同的狀態(tài)類(lèi)實(shí)例的時(shí)候context.do的執(zhí)行結(jié)果不同。
狀態(tài)模式將對(duì)象的行為封裝到不同的狀態(tài)類(lèi)中,使得對(duì)象在不同狀態(tài)下可以選擇不同的行為。
這句話理解成:
本來(lái)應(yīng)該在context.do中通過(guò)if else實(shí)現(xiàn)的功能,被封裝到了不同的狀態(tài)類(lèi)中(StateA StateB);
當(dāng)context的狀態(tài)改變的時(shí)候(即:context.cts引用不同的狀態(tài)類(lèi)實(shí)例的時(shí)候)context.do也能實(shí)現(xiàn)封裝前相同的功能(即表現(xiàn)出不同的行為)。
見(jiàn)下面代碼:
不使用狀態(tài)設(shè)計(jì)模式實(shí)現(xiàn)上述代碼功能:
class Context { ? state = 'A'; ? to(state: string) { ? ? console.log(`Transitioning to state ${state}`); ? ? this.state = state; ? } ? do() { ? ? if(this.state === 'A'){ ? ? ? console.log("買(mǎi)票"); ? ? ? this.to('B'); ? ? } else if (this.state === 'B') { ? ? ? console.log("坐車(chē)"); ? ? ? this.to('C'); ? ? } else if (this.state === 'C') { ? ? ? console.log("看球"); ? ? ? this.to('D'); ? ? } else if (this.state === 'D') { ? ? ? throw new Error('已經(jīng)結(jié)束了'); ? ? } ? } } const context = new Context(); while(1){ ? try { ? ? context.do(); ? } catch { ? ? console.log('肯定輸了!'); ? ? break; ? } }
【對(duì)比】雖然這樣看起來(lái)代碼量好像少了一些,但是如果老鐵想要在下車(chē)后去趟衛(wèi)生間,使用了狀態(tài)設(shè)計(jì)模式的代碼只需要修改StateB的實(shí)現(xiàn)就可以了,而后者則必須修改Context中的do方法!
優(yōu)點(diǎn)
封裝之后的狀態(tài)可以獨(dú)立變化,符合開(kāi)閉原則。
狀態(tài)模式提供了一種清晰的方式來(lái)組織對(duì)象的行為,并避免了使用大量的條件語(yǔ)句。
缺點(diǎn)
狀態(tài)較多時(shí),導(dǎo)致?tīng)顟B(tài)類(lèi)的數(shù)量增加,增加系統(tǒng)的復(fù)雜性。
對(duì)象在不同狀態(tài)下的轉(zhuǎn)換邏輯可能會(huì)分散在多個(gè)狀態(tài)類(lèi)中,使得代碼難以理解和維護(hù)。
如果某個(gè)狀態(tài)只被一個(gè)對(duì)象使用,可以考慮使用策略模式替代狀態(tài)模式。
應(yīng)用
狀態(tài)模式常見(jiàn)于需要根據(jù)對(duì)象的內(nèi)部狀態(tài)來(lái)改變其行為的場(chǎng)景,例如【訂單狀態(tài)的處理】、【游戲角色的狀態(tài)轉(zhuǎn)換】等。
它提供了一種可擴(kuò)展的方式來(lái)處理狀態(tài)相關(guān)的邏輯,并促進(jìn)了代碼的靈活性和可維護(hù)性。
generator函數(shù)可以看成原生js實(shí)現(xiàn)了狀態(tài)設(shè)計(jì)模式。
感性記憶
同一個(gè)對(duì)象,當(dāng)其內(nèi)部維護(hù)的狀態(tài)不同的時(shí)候,調(diào)用其上的同一個(gè)方法,可能會(huì)得到完全不同的結(jié)果。
總結(jié)
就是適用于同一個(gè)對(duì)象具有不同的狀態(tài),并且在各個(gè)狀態(tài)下表現(xiàn)不一致的情況;這些狀態(tài)之間如果是可以相互轉(zhuǎn)換的,使用此設(shè)計(jì)模式就更加合適了。
到此這篇關(guān)于JS設(shè)計(jì)模式之狀態(tài)模式的用法使用方法的文章就介紹到這了,更多相關(guān)JS狀態(tài)模式使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺析JavaScriptSerializer類(lèi)的序列化與反序列化
本篇文章主要介紹了JavaScriptSerializer類(lèi) 對(duì)象序列化與反序列化的方法、屬性以及實(shí)例代碼,有需要的朋友可以參考一下2016-11-11electron中獲取mac地址的實(shí)現(xiàn)示例
在基于Electron的應(yīng)用中,有一個(gè)業(yè)務(wù)需求是獲取物理網(wǎng)卡的Mac地址以用于客戶(hù)機(jī)唯一性識(shí)別,本文主要介紹了electron中獲取mac地址的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12詳解微信圖片防盜鏈“此圖片來(lái)自微信公眾平臺(tái) 未經(jīng)允許不得引用”的解決方案
這篇文章主要介紹了詳解微信圖片防盜鏈“此圖片來(lái)自微信公眾平臺(tái) 未經(jīng)允許不得引用”的解決方案,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-04-04bootstrap modal+gridview實(shí)現(xiàn)彈出框效果
這篇文章主要介紹了bootstrap modal+gridview實(shí)現(xiàn)彈出框效果,gridview點(diǎn)擊更新彈出填寫(xiě)信息表單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08三分鐘學(xué)會(huì)用ES7中的Async/Await進(jìn)行異步編程
這篇文章主要介紹了三分鐘學(xué)會(huì)用ES7中的Async/Await進(jìn)行異步編程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06JS點(diǎn)擊某個(gè)圖標(biāo)或按鈕彈出文件選擇框的實(shí)現(xiàn)代碼
本文給大家介紹js點(diǎn)擊點(diǎn)擊某個(gè)圖標(biāo)或按鈕彈出文件選擇框的實(shí)現(xiàn)代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),感興趣的朋友可以參考下2016-09-09如何理解JS函數(shù)防抖和函數(shù)節(jié)流
函數(shù)防抖和函數(shù)節(jié)流都是對(duì)函數(shù)進(jìn)行特殊的設(shè)置,減少該函數(shù)在某一時(shí)間段內(nèi)頻繁觸發(fā)帶來(lái)的副作用。二者只是采用的設(shè)置方式和原理不一樣,其最終的目的是一樣的。2021-05-05javascript實(shí)現(xiàn)五星評(píng)價(jià)代碼(源碼下載)
大家在淘寶購(gòu)物之后,都會(huì)對(duì)賣(mài)家的服務(wù)進(jìn)行評(píng)論,那么五星評(píng)價(jià)代碼是怎么實(shí)現(xiàn)的呢?下面小編給大家介紹基于Javascript實(shí)現(xiàn)五星評(píng)價(jià)代碼,有需要的朋友可以參考下2015-08-08