iOS設計模式之狀態模式

語言: CN / TW / HK

持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第25天,點選檢視活動詳情

本文主要介紹iOS設計模中的狀態模式,狀態模式顧名思義就是通過改變物件的狀態從而改變物件的行為。

1. 什麼是狀態模式

我們通過定義物件的狀態從而改變物件的行為,並且你還可將該方法應用在物件上。 假如你有一個 文件Document類。 文件可能會處於 草稿Draft 、  審閱中Moderation和 已釋出Published三種狀態中的一種。 文件的 publish釋出方法在不同狀態下的行為略有不同:

  • 處於 草稿狀態時, 它會將文件轉移到審閱中狀態。
  • 處於 審閱中狀態時, 如果當前使用者是管理員, 它會公開發布文件。
  • 處於 已釋出狀態時, 它不會進行任何操作。

狀態機通常由眾多條件運算子 ( if或 switch ) 實現, 可根據物件的當前狀態選擇相應的行為。  “狀態” 通常只是物件中的一組成員變數值。 即使你之前從未聽說過有限狀態機, 你也很可能已經實現過狀態模式。

智慧手機的按鍵和開關會根據裝置當前狀態完成不同行為

  • 當手機處於解鎖狀態時, 按下按鍵將執行各種功能。
  • 當手機處於鎖定狀態時, 按下任何按鍵都將解鎖螢幕。
  • 當手機電量不足時, 按下任何按鍵都將顯示充電頁面。 狀態模式建議為物件的所有可能狀態新建一個類, 然後將所有狀態的對應行為抽取到這些類中。

原始物件被稱為上下文(context), 它並不會自行實現所有行為, 而是會儲存一個指向表示當前狀態的狀態物件的引用, 且將所有與狀態相關的工作委派給該物件。

狀態是一種行為設計模式, 讓你能在一個物件的內部狀態變化時改變其行為。

2. 什麼時候使用狀態模式

以下這些情況可以考慮使用狀態模式 - [ ] 如果物件需要根據自身當前狀態進行不同行為, 同時狀態的數量非常多且與狀態相關的程式碼會頻繁變更的話, 可使用狀態模式。 - [ ] 如果某個類需要根據成員變數的當前值改變自身行為, 從而需要使用大量的條件語句時, 可使用該模式。 - [ ]  當相似狀態和基於條件的狀態機轉換中存在許多重複程式碼時, 可使用狀態模式。

3. 程式碼展示

```Swift import XCTest

/// The Context defines the interface of interest to clients. It also maintains /// a reference to an instance of a State subclass, which represents the current /// state of the Context. class Context {

/// A reference to the current state of the Context.
private var state: State

init(_ state: State) {
    self.state = state
    transitionTo(state: state)
}

/// The Context allows changing the State object at runtime.
func transitionTo(state: State) {
    print("Context: Transition to " + String(describing: state))
    self.state = state
    self.state.update(context: self)
}

/// The Context delegates part of its behavior to the current State object.
func request1() {
    state.handle1()
}

func request2() {
    state.handle2()
}

}

/// The base State class declares methods that all Concrete State should /// implement and also provides a backreference to the Context object, /// associated with the State. This backreference can be used by States to /// transition the Context to another State. protocol State: class {

func update(context: Context)

func handle1()
func handle2()

}

class BaseState: State {

private(set) weak var context: Context?

func update(context: Context) {
    self.context = context
}

func handle1() {}
func handle2() {}

}

/// Concrete States implement various behaviors, associated with a state of the /// Context. class ConcreteStateA: BaseState {

override func handle1() {
    print("ConcreteStateA handles request1.")
    print("ConcreteStateA wants to change the state of the context.\n")
    context?.transitionTo(state: ConcreteStateB())
}

override func handle2() {
    print("ConcreteStateA handles request2.\n")
}

}

class ConcreteStateB: BaseState {

override func handle1() {
    print("ConcreteStateB handles request1.\n")
}

override func handle2() {
    print("ConcreteStateB handles request2.")
    print("ConcreteStateB wants to change the state of the context.\n")
    context?.transitionTo(state: ConcreteStateA())
}

}

/// Let's see how it all works together. class StateConceptual: XCTestCase {

func test() {
    let context = Context(ConcreteStateA())
    context.request1()
    context.request2()
}

} 執行結果Swift Context: Transition to StateConceptual.ConcreteStateA ConcreteStateA handles request1. ConcreteStateA wants to change the state of the context.

Context: Transition to StateConceptual.ConcreteStateB ConcreteStateB handles request2. ConcreteStateB wants to change the state of the context.

Context: Transition to StateConceptual.ConcreteStateA ```

4. 小結

我們物件存在大量狀態的時候,當前狀態完成不同行為。或者存在很多條件判斷對應不同狀態,或者是相似狀態和基於條件的狀態機轉換。都可以採用狀態模式。比如我們客戶端使用者有很多狀態,我們獲取使用者不同狀態從而跳轉不同頁面或者一些操作。