一種基於狀態機的代碼設計

最近在學習 SwiftUI ,寫了一個計算器的 Demo。寫完後學習並理解到一種代碼設計,感受頗有收穫,在此記錄一下。git

一句話歸納

咱們將 App 看成一個狀態機,狀態機的狀態決定 App 的界面。首先咱們理出 App 都有什麼狀態?每一個狀態須要什麼樣的條件會觸發什麼樣的 Action?Action 執行後狀態機會進入什麼樣的次態?理清這些後,根據設計,隨着狀態機狀態的改變,,App 的界面天然會跟着更新。github

以計算器爲例

在這裏我用我寫的計算器 Demo 爲例講解下這種方式。swift

找出狀態

計算器的業務邏輯就是接受用戶的輸入的算式,而後計算並輸出算式的結果。那麼計算器的狀態就是由算式的狀態來決定。markdown

首先看看算式的組成:app

「左側數字 + 計算符號 + 右側數字 + 計算符號或者等號」oop

看着算式的結構,咱們就能捋出計算器都有什麼狀態:學習

  1. 計算器正在輸入左側數字,這個狀態下用戶按了計算符號按鈕後會進入下一個狀態。
  2. 計算器已經輸入了左側數字和計算符號,開始等待右側符號
  3. 計算器已經輸入了左側數字和計算符號,正在接收右側數字(注意和 2 的區別,一個是符號,一個是數字
  4. 計算器已經計算出結果,開始等待符號(可能爲新的計算,也可能以這次結果繼續進行計算)
  5. 輸入或計算出現錯誤,沒法繼續

因此咱們須要在代碼中定義出五個狀態:編碼

// 這五個枚舉和上面定義的狀態按順序一一對應
enum CalculatorState {
  case left(String)
  case leftOp(left: String, op: Calculator.Op)
  case leftOpRight(left: String, op: Calculator.Op, right: String)
  case equal(value: String)
  case error
}
複製代碼

狀態咱們已經定義出來了,接下來就須要定義改變狀態時須要的 Action 了。spa

須要什麼樣的 Action 才能進入狀態

這裏主要以狀態 1(left)爲例,講解在狀態 1 下遇到什麼條件會觸發什麼樣的 Action,而後會進入哪一個狀態。設計

  1. 若是接收到了數字的輸入,此時狀態並不會發生改變,仍是處於 left,只不過這個狀態下的數據會發生改變
  2. 若是接收到了運算符的輸入,此時就會進入狀態 2(leftOp),並且狀態 2 的數據是狀態 1 的值加上剛剛輸入的運算符(Calculator.Op)
  3. 若是接收到了等號,一個算式的生命週期會結束,進入狀態 4(equal),等待從新開始一次計算。

語言是蒼白的,直接看代碼:

// 因爲代碼過長,所有附上會影響篇幅,此處僅附上部分代碼。詳細代碼能夠在文末找到連接
func apply(item: CalculatorButtonItem) -> CalculatorState {
  switch item {
    case .digit(let num):
    return apply(num: num)
    case .dot:
    return applyDot()
    case .op(let op):
    return apply(op: op)
    case .command(let command):
    return apply(command: command)
  }
}

private func apply(num: Int) -> CalculatorBarin {
  switch self {
    case .left(let left):
    return .left(left.apply(num: num))
    case .leftOp(let left, let op):
    return .leftOpRight(left: left, op: op, right: "0".apply(num: num))
    case .leftOpRight(let left, let op, let right):
    return .leftOpRight(left: left, op: op, right: right.apply(num: num))
    case .equal(_):
    return .left("0".apply(num: num))
    case .error:
    return .left("0".apply(num: num))
  }
}
複製代碼

上面只解釋了狀態 1 進入次態須要的條件和進入次態前會作的 Action,其餘的狀態其實都是相似的,此處就再也不贅述。

其餘場景

計算器的業務比較簡單,用來闡述代碼設計會清晰易懂一些。這種代碼設計並非萬金油,哪裏都能用,不過確定也有不少比較適用的場景。好比應用商店的詳情頁面,詳情頁面的狀態就比較多,並且狀態和狀態之間的區別比較清晰,用這種方式來處理就比較合適。

侷限

固然這種設計也是有他的侷限所在。

好比當業務比較複雜的時候可能會由於發生狀態數量爆炸,此時代碼應該就會失控了。其餘的缺陷暫時尚未想到,等我進行一次最佳實踐後可能會發現更多的問題。

天底下不存在一個絕對正確的設計,都須要根據實際狀況本身作出相應的調整,以不變應萬變~

最後

本文完整代碼地址:github.com/T-Oner/Swif…

我在學習 SwiftUI,如何你也對這個感興趣,那麼我推薦這本我在用的教材:

掃描二維碼查看教材

本文僅爲我的意見,不構成編碼建議。

相關文章
相關標籤/搜索