摘要:有限狀態機可以歸納出四個要素現態即當前的狀態。但狀態模式還有一點需要注意到,當采用子類繼承實現多種具體狀態的時候,注意控制狀態的數量,以免出現子類數量膨脹的現象在使用或等更完整面向對象語言時。
業務代碼開發久了,偶爾看看設計模式,總會讓自己有一種清新脫俗的感覺。總想把這種感覺記下來,但一想到要先起個恰如其分的標題和開頭,就讓我有一種百爪撓心的糾結,所以遲遲沒有開始。今天起更新我學習設計模式筆記的原因,就好像是,你喜歡一個女孩久了,卻總不表白,難道不怕被別人截胡了么!
首先我們來一起設想一些場景:
程序員們在等電梯的時候,聊天頻率最高的一個話題,是不是電梯調度算法呢。寫字樓的幾部電梯到底是分單雙層運力快,還是高低層運力快?當你按下電梯時,是就近樓層的電梯視來接你,還是自顧自的先上后下順帶來接你?
開車來到路口,對紅綠燈亮滅的長短是否曾有過習慣性的吐槽
除了電梯調度、紅綠燈控制,軟件設計和業務開發中,類似諸如狀態切換的問題不難遇到。他們的共同點是:場景存在多個狀態,狀態改變時會觸發對應的不同處理方法,狀態間切換又存在諸多約束和限制。
面對這種場景,你腦海里的第一解決方案是什么?條件分支if...else或者switch...case么?其實應當視具體場景復雜度來看,如果狀態少邏輯簡單,條件分支力所能及。但倘若狀態較多,邏輯復雜,又存在諸多特殊情況的約束限制,本文將介紹的狀態模式歡迎來解一下。
狀態模式定義:當一個對象的內在狀態改變時,允許改變其行為,這個對象看起來像是改變了其類
我們先來看下傳統面向對象語言的類圖,后面再細說前端js中該如何應用
對于一個if...else處理的長流程,我們可以抽象成多個狀態的切換,不同具體的狀態繼承自一個抽象狀態接口。場景類維護當前狀態對象的一個實例,以及狀態切換間的約束關系。
這樣做的好處是將狀態的獲取和狀態的切換進行了分離,一個具體的狀態類只處理本狀態相關的邏輯,符合單一職責原則,后期如果新加狀態只需要新建具體的狀態類,符合開放封閉原則。
JavaScript沒有抽象接口類的概念,所有類圖也大大簡化,如下:
State類為狀態類,包含狀態值以及狀態改變時具體的處理方法。Context類為場景類,維護狀態間的約束關系以及控制觸發狀態的切換。
下面我們看下代碼,讓概念平穩落地:
// 定義狀態類 class State { constructor(color) { this.color = color } // 處理該狀態下具體邏輯 handle(context) { console.log(`turn to ${this.color}`) context.setState(this) } }
// 定義場景類 class Context { constructor() { this.state = null } setState(state) { this.state = state } getState() { return this.state } }
測試代碼如下:
const ctx = new Context() // 實例出具體狀態 const red = new State("red") const green = new State("green") const yellow = new State("yellow") // 綠燈亮 green.handle(ctx) console.log(ctx.getState()) // 紅燈亮 red.handle(ctx) console.log(ctx.getState()) // 黃燈亮 yellow.handle(ctx) console.log(ctx.getState())有限狀態機
狀態模式脫胎自有限狀態機(Finite-State-Machine),這個數學模型描述了有限個狀態,以及這些狀態之間轉移和動作的行為,是一種對象行為建模的工具。類似下圖就是一種有限狀態機:
其實我們在處理業務邏輯時,經常打交道的各種事件和狀態切換,寫的各種if...else和switch...case都是有限狀態機模型,只是平時沒有意識到吧了。在處理較為復雜的邏輯時,考慮把業務邏輯抽象成一個有限狀態機模型,常常會是代碼邏輯清晰,結構規整。
有限狀態機可以歸納出四個要素:
現態:即當前的狀態。
條件:又稱為“事件”。當一個條件被滿足,將會觸發一個動作,或者執行一次狀態的遷移。
動作:條件滿足后執行的動作。動作執行完畢后,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足后,也可以不執行任何動作,直接遷移到新狀態。
次態:條件滿足后要遷往的新狀態。次態是相對于現態而言的,次態一旦被激活,就轉變成新的現態了。
Tips:避免把某個程序動作當作是一種狀態來處理,動作是不穩定的,即使條件沒有觸發,一旦動作執行完也就結束了;但狀態是穩定的,如果沒有外部條件觸發,狀態會一直持續下去。
介紹了有限狀態機,我們當然可以通過上面介紹的狀態模式的方式,來將這種模型工具應用到我們的代碼開發當中。但是你有沒有注意到一個問題?對,代碼不夠優雅,略顯簡陋,不能忍!
接下來介紹一個優雅的有限狀態機實現類庫javascript-state-machine,接下來使用這個類庫簡單實現一個Promise的功能,來看一下如何使用。
Promise簡單實現首先回顧一下Promise的特點:
Promise是一個類。
Promise在實例初始化的時候需要傳入一個函數。
傳入的函數需要接收resolve和reject兩個函數,成功的時候調用resolve,失敗的時候調用reject。
Promise實例出的對象有一個then方法,可以進行鏈式操作。
Promise擁有三種狀態:pending、fulfilled、rejected,可以從pending->fulfilled,或pending->rejected,但不能逆向。
接下來上代碼實現一下
// 狀態機模型 const fsm = new StateMachine({ // 初始狀態 init: "pending", // 狀態遷移規則,name,from,to的名字盡量別同名 transitions: [ { name: "resolve", from: "pending", to: "fulfilled" }, { name: "reject", from: "pending", to: "rejected"} ], methods: { onResolve(state, data) { data.successFn.forEach(fn => fn()) }, onReject(state, data) { data.failFn.forEach(fn => fn()) } } })
// 定義Promise class MyPromise { constructor(fn) { this.successFn = [] this.failFn = [] fn( () => { fsm.resolve(this)}, () => { fsm.reject(this)} ) } then(successFn, failFn) { this.successFn.push(successFn) this.failFn.push(failFn) return this } }總結
本文介紹了狀態模式和有限狀態機的概念,以及才開發中優雅使用的姿勢javascript-state-machine,并通過用其簡單實現了Promise的基本功能,演示了如何使用。
其實重點還是狀態模式,通過本文介紹可以很明顯地感受到其優點:
結構清晰,避免了過多的if...else或switch...case的使用,降低了程序的復雜性,提高了系統的可維護性。
很好遵循了開放封閉原則和單一職責原則。
記得Martin在《重構》中,提到一個壞的代碼味道“Long Method”,當你遇到一個方法中包含了一大堆邏輯,做了很多事的時候,你就應該嗅探到一股惡臭味,怎么去修改,或許考慮使用狀態模式是一條途徑。
但狀態模式還有一點需要注意到,當采用子類繼承實現多種具體狀態的時候,注意控制狀態的數量,以免出現子類數量膨脹的現象(在使用TypeScript或Java等更完整面向對象語言時)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99129.html
摘要:函數式編程前端掘金引言面向對象編程一直以來都是中的主導范式。函數式編程是一種強調減少對程序外部狀態產生改變的方式。 JavaScript 函數式編程 - 前端 - 掘金引言 面向對象編程一直以來都是JavaScript中的主導范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數式編程越來越多得受到開發者的青睞。函數式編程是一種強調減少對程序外部狀態產生改變的方式。因此,...
摘要:代理模式,迭代器模式,單例模式,裝飾者模式最少知識原則一個軟件實體應當盡可能少地與其他實體發生相互作用。迭代器模式可以將迭代的過程從業務邏輯中分離出來,在使用迭代器模式之后,即不用關心對象內部構造也可以按順序訪問其中的每個元素。 接手項目越來越復雜的時候,有時寫完一段代碼,總感覺代碼還有優化的空間,卻不知道從何處去下手。設計模式主要目的是提升代碼可擴展性以及可閱讀性。 本文主要以例子的...
摘要:至于如何優雅地管理使用,再次祭出潘神的文章手摸手,帶你優雅的使用掘金項目的后端接口文檔我是用的進行的管理,其實有很多強大的功能,不僅僅是一個接口測試工具,接口文檔管理就是其中一個。 首先放個線上地址大家感受一下(由于后端用的是 leancloud 的免費套餐,因此可能會比較慢): vue-data-board P.S. 建議大家盡量自己注冊一個賬號(可以隨便填一個密碼),如果用默認的測...
閱讀 2831·2023-04-26 02:23
閱讀 1570·2021-11-11 16:55
閱讀 3149·2021-10-19 11:47
閱讀 3352·2021-09-22 15:15
閱讀 1975·2019-08-30 15:55
閱讀 1033·2019-08-29 15:43
閱讀 1288·2019-08-29 13:16
閱讀 2188·2019-08-29 12:38