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

