<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何在Swift中實(shí)現(xiàn)狀態(tài)機(jī)?

          共 25638字,需瀏覽 52分鐘

           ·

          2022-04-09 07:24

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


          作者丨搜狐焦點(diǎn)-向輝

          來源丨搜狐技術(shù)產(chǎn)品(ID:sohu-tech)


             

          本文字?jǐn)?shù):4593

          預(yù)計(jì)閱讀時(shí)間:14分鐘

          序章

          什么是狀態(tài)機(jī)?

          我們還是直接使用wikiPedia上的定義:

          ?

          A finite-state machine (FSM) or finite-state automaton (FSA, plural: automata), finite automatonor simply a state machine, is a mathematical model of computation.

          簡言之:我們通常稱作的狀態(tài)機(jī)是有限狀態(tài)機(jī)的簡稱,它是一種數(shù)學(xué)計(jì)算模型。

          有限狀態(tài)機(jī)(也就是有限自動機(jī))如果進(jìn)行一個(gè)分類的話,分類如下:

          這張圖只是看一個(gè)大概的分類,實(shí)際上這個(gè)有一個(gè)簡單的認(rèn)知就可以了,因?yàn)槲覀円接懙姆N類主要就是DFA(Deterministic Finite Automaton):確定性有限狀態(tài)自動機(jī)。它也是最簡單的一種狀態(tài)機(jī)模型,下文中我們簡稱為狀態(tài)機(jī)的都是確定性有限狀態(tài)自動機(jī)。

          ?

          注:分類本身是有各種分類方式的,這里采取是以是否有輸出作為分類的前提,Wikipedia上的分類方式和此種分類不太一致,大家可以參考對比一下。

          那么確定性有限狀態(tài)自動機(jī)有哪些特點(diǎn)呢?簡單來講就是每一種輸入引起狀態(tài)的變化是確定的,即一個(gè)狀態(tài)對于同樣的輸入,只能有一種轉(zhuǎn)換規(guī)則。

          從一個(gè)簡易的狀態(tài)機(jī)模型開始

          很多人每天上班都要刷卡進(jìn)出地鐵,我們就以有旋轉(zhuǎn)柵門的地鐵站閘機(jī)入口作為例子。

          這個(gè)閘機(jī)口在開始的時(shí)候有一個(gè)“l(fā)ocked”的狀態(tài), 在這個(gè)狀態(tài)下它并不會讓乘客旋轉(zhuǎn)柵欄通過進(jìn)站口。當(dāng)有人刷卡了,那么這個(gè)閘機(jī)口的狀態(tài)就會變?yōu)椤皍nlocked”,但是它并不會自己轉(zhuǎn)動,必須得等到有人推動旋轉(zhuǎn)柵欄通過閘機(jī)口,且在這之后閘機(jī)口的狀態(tài)會再次變?yōu)椤發(fā)ocked”。

          從上面的描述中可以看到這個(gè)閘機(jī)口總是處于兩種狀態(tài)之一:Locked 或者 Unlocked。同時(shí)也只有兩種轉(zhuǎn)化的觸發(fā)條件:刷卡 或者 通過旋轉(zhuǎn)柵欄。

          接下來,我們可以通過圖形的方式來給這個(gè)閘機(jī)系統(tǒng)建模,使用圓角矩形代表狀態(tài),使用箭頭代表狀態(tài)之間的轉(zhuǎn)換:

          上述系統(tǒng)大概描述出了該系統(tǒng)是如何工作的,但是還有一些場景是這個(gè)系統(tǒng)無法明確定義的。比如刷了卡之后馬上又刷了一次卡,那么狀態(tài)如何轉(zhuǎn)變呢?比如還沒有刷卡之前就有人要通過旋轉(zhuǎn)欄,那么狀態(tài)是如何轉(zhuǎn)變呢?在上述的兩種情況,我們建的狀態(tài)機(jī)模型會保留在它的當(dāng)前狀態(tài)。

          同時(shí)我們還得給這個(gè)系統(tǒng)加上一個(gè)初始的狀態(tài),這樣我們就可以繼續(xù)完善這個(gè)模型了:

          有限狀態(tài)機(jī)的定義

          上述模型就是一個(gè)簡單的狀態(tài)機(jī)模型,基于此,我們可以從中總結(jié)出這個(gè)系統(tǒng)幾個(gè)關(guān)鍵的特征:

          • 這個(gè)系統(tǒng)包含有限的幾個(gè)狀態(tài)
          • 這個(gè)系統(tǒng)包含有限的幾個(gè)輸入(或者事件)可以觸發(fā)狀態(tài)之間的轉(zhuǎn)移
          • 這個(gè)系統(tǒng)有一個(gè)特定的初始狀態(tài)
          • 這個(gè)系統(tǒng)在某一時(shí)刻的行為取決于當(dāng)前的狀態(tài)以及當(dāng)前的輸入
          • 對于系統(tǒng)可能處于的每一個(gè)狀態(tài),每一個(gè)輸入(或者事件)都會有對于的行為

          ?

          ? The system must be describable by a finite set of states. 

          ? The system must have a finite set of inputs and/or events that can trigger transitions between states. 

          ? The system has a particular initial state 

          ? The behavior of the system at a given point in time depends upon the current state and the input or event that occur at that time. 

          ? For each state the system may be in, behavior is defined for each possible input or event.

          這個(gè)時(shí)候我們的定義已經(jīng)夠多了,有些理論,但是還是還不夠,作為工程師,我們通常需要使用更加正式的,或者說更數(shù)學(xué)的方式來描述該系統(tǒng),一個(gè)有限自動機(jī)M通常被五元組(Σ, Q, q0, F, δ)所定義:

          • Σ is the set of symbols representing input to M
          • Q is the set of states of M
          • q0 ∈ Q is the start state of M
          • F ? Q is the set of final states of M
          • δ : Q × Σ → Q is the transition function

          簡單來說,該五元組為(輸入,狀態(tài)集,初始狀態(tài),結(jié)果狀態(tài)集,轉(zhuǎn)換方法), 那么這里的轉(zhuǎn)換方法如何理解呢?它的參數(shù)就是當(dāng)前狀態(tài) 以及 輸入 輸出就是 結(jié)果狀態(tài) 即 δ(q, x) = p。一個(gè)有限狀態(tài)機(jī) M 處于狀態(tài) q 中,接受到 x 輸入,在處理完轉(zhuǎn)換過程中的Action之后,就會變?yōu)闋顟B(tài) p 。

          而對于這個(gè)轉(zhuǎn)化方法的抽象,在我們自定義狀態(tài)機(jī)的時(shí)候?qū)⒂葹橹匾?/p>

          中章

          實(shí)現(xiàn)狀態(tài)機(jī)最簡單的方法是使用枚舉類和swtich-case語句來實(shí)現(xiàn),但是我們今天不會使用這種方式,而是會嘗試用兩種不同的方式來定義一個(gè)狀態(tài)機(jī)。

          如何實(shí)現(xiàn)狀態(tài)機(jī) - 面向?qū)ο螅∣OP)

          基本抽象

          第一種方式是使用OOP的思路來完成一個(gè)通用的狀態(tài)機(jī),那么首當(dāng)其沖的要考慮可以抽象出哪些類呢?結(jié)合上方的五元組,抽象如下:

          protocol EventHashable { }

          class State<EEvent{

          }

          class StateMachine<EEvent{
              
          }

          第一是事件:因?yàn)闋顟B(tài)的轉(zhuǎn)移是需要事件觸發(fā)的,所以事件集在這里定義為一個(gè)Event協(xié)議,后面誰使用,誰定義相應(yīng)的事件枚舉即可。

          第二是狀態(tài):這里把五元組中的Transition Function封裝進(jìn)狀態(tài)類,直接由狀態(tài)類本身來管理轉(zhuǎn)換行為。

          第三是狀態(tài)機(jī):這個(gè)就類似我們MVC中的view controller,是狀態(tài)機(jī)的中樞,需要由它來管理狀態(tài)的切換。

          以上就是我定義的狀態(tài)機(jī)模型的基建了,那么我們整體的設(shè)計(jì)思路呢?

          1、狀態(tài)機(jī)類接受事件觸發(fā) 

          2、將事件傳遞給當(dāng)前狀態(tài),由當(dāng)前狀態(tài)觸發(fā)事件 

          3、事件觸發(fā)后:如果需要跳轉(zhuǎn)到其它狀態(tài),當(dāng)前狀態(tài)離開,而目標(biāo)狀態(tài)進(jìn)入

          設(shè)計(jì)非常簡單,接下來看具體的實(shí)現(xiàn)。

          狀態(tài)類

          class State<EEvent{

              weak open var stateMachine: StateMachine<E>?

              open func trigger(event: E) {}

              open func enter() {
                  stateMachine?.currentState = self
              }

              open func exit() { }
          }

          這里來分析一下具體有什么:

          • 狀態(tài)機(jī)屬性

          當(dāng)狀態(tài)機(jī)傳入事件的時(shí)候,當(dāng)前狀態(tài)在進(jìn)行事件判斷之后,需要調(diào)用狀態(tài)機(jī)以進(jìn)入目標(biāo)狀態(tài)。

          • 觸發(fā)條件

          此處相當(dāng)于注冊transition!當(dāng)前狀態(tài)接受每一個(gè)輸入事件的轉(zhuǎn)移都將注冊在此處。

          • Transition 方法

          具體來說就是當(dāng)前狀態(tài)的離開方法,以及目標(biāo)狀態(tài)的進(jìn)入方法。

          狀態(tài)機(jī)類

          class StateMachine<EEvent{
              typealias FSMState = State<E>
              
              open var currentState: FSMState!
              
              private var states: [FSMState] = []
              
              init(initialState: FSMState) {
                  currentState = initialState
              }
              
              open func setupStates(_states: [FSMState]) {
                  states = _states
                  
                  states.forEach { $0.stateMachine = self }
              }
              
              open func trigger(event: E) {
                  currentState.trigger(event: event)
              }
              
              open func enter(_ stateClass: AnyClass) {
                  states.forEach {
                      if type(of: $0) == stateClass {
                          $0.enter()
                      }
                  }
              }
          }

          那么接下來分析一下具體需要些什么:

          • 構(gòu)造方法

          對于狀態(tài)機(jī)類,我們需要給它一個(gè)初始狀態(tài),這樣的話,它的構(gòu)造方法就需要一個(gè)初始狀態(tài)的參數(shù)。

          • 狀態(tài)集

          因?yàn)闋顟B(tài)機(jī)需要管理狀態(tài)之間的轉(zhuǎn)移,那么它就需要存儲這些狀態(tài)。

          • 觸發(fā)方法

          外界事件觸發(fā)的時(shí)候,狀態(tài)機(jī)就觸發(fā)當(dāng)前狀態(tài)的事件檢測方法,因?yàn)檫@里,我們是讓每一個(gè)狀態(tài)內(nèi)部來針對不同的輸入,來完成對應(yīng)的轉(zhuǎn)換規(guī)則的。

          • 轉(zhuǎn)換狀態(tài)方法

          當(dāng)要進(jìn)入新狀態(tài)時(shí),依照上述的設(shè)計(jì),我們需要通過狀態(tài)機(jī)類來處理進(jìn)入新狀態(tài)的方法。

          如何使用?

          當(dāng)我去思考哪些軟件可能會使用狀態(tài)機(jī)的時(shí)候,第一反應(yīng)就是游戲,因?yàn)橛螒蛑幸粋€(gè)角色有各種各樣的狀態(tài),如果是使用if-else的話那未免太過于繁瑣,這個(gè)時(shí)候,通過狀態(tài)機(jī)來控制狀態(tài)的變化一定是最優(yōu)解,比如idle狀態(tài),walk狀態(tài),run狀態(tài),attack狀態(tài),hurt狀態(tài)等等等等。

          所以這里我們只用一個(gè)簡單的例子,通過兩個(gè)按鈕來控制角色walk和run的狀態(tài)切換。

          所以使用的時(shí)候先定義事件

          enum RoleEventEvent {
              case clickRunButton
              case clickWalkButton
          }

          再定義狀態(tài),這里我們定義RunState 和 WalkState:

          class RunStateState<RoleEvent{
              override func trigger(event: RoleEvent) {
                  super.trigger(event: event)
                  
                  switch event {
                  case .clickRunButton:
                      break
                  case .clickWalkButton:
                      self.exit()
                      stateMachine?.enter(WalkState.self)
                  }
              }
              
              override func enter() {
                  super.enter()
                  
                  NSLog("====run enter=====")
              }
              
              override func exit() {
                  super.exit()
                  
                  NSLog("====run exit=====")
              }
          }

          class WalkStateState<RoleEvent{
              override func trigger(event: RoleEvent) {
                  super.trigger(event: event)
                  
                  switch event {
                  case .clickRunButton:
                      self.exit()
                      stateMachine?.enter(RunState.self)
                  case .clickWalkButton:
                      break
                  }
              }
              override func enter() {
                  super.enter()
                  
                  NSLog("====walk enter=====")
              }
              
              override func exit() {
                  super.exit()
                  
                  NSLog("====walk exit=====")
              }
          }

          最后就是初始化了:

          func initialStateMachine() {
              let run = RunState.init()
              let walk = WalkState.init()

              stateMachine = StateMachine.init(initialState: run)
              stateMachine.setupStates(_states: [run, walk])
          }

          然后實(shí)際使用的時(shí)候,通過stateMachine觸發(fā)不同的事件即可:

          stateMachine.trigger(event: .clickRunButton)
          // stateMachine.trigger(event: .clickWalkButton)

          如何實(shí)現(xiàn)狀態(tài)機(jī) - 函數(shù)式

          這里我參考了一個(gè)很輕量級的Swift狀態(tài)機(jī)框架Stateful來實(shí)現(xiàn)一個(gè)更swift風(fēng)格的狀態(tài)機(jī)。

          Transition

          我們先拋開OOP的思路,回想一下確定性有限自動機(jī)的數(shù)學(xué)定義,有一個(gè)東西很關(guān)鍵,那就是transition function,所以這里我們先把transition抽象為一個(gè)結(jié)構(gòu)體,包含一個(gè)轉(zhuǎn)換規(guī)則的所有元素。

          public typealias ExecutionBlock = (() -> Void)
          public struct Transition<StateEvent{
              public let event: Event
              public let source: State
              public let destination: State
              let preAction: ExecutionBlock?
              let postAction: ExecutionBlock?
              
              public init(with event: Event,
                          from: State,
                          to: State,
                          preBlock: ExecutionBlock?,
                          postBlock: ExecutionBlock?) {
                  self.event = event
                  self.source = from
                  self.destination = to
                  self.preAction = preBlock
                  self.postAction = postBlock
              }
              
              func executePreAction() {
                  preAction?()
              }
              
              func executePostAction() {
                  postAction?()
              }
          }

          來看這一段代碼的話,會發(fā)現(xiàn),我們將觸發(fā)事件,開始狀態(tài),結(jié)束狀態(tài),以及轉(zhuǎn)換過程中需要執(zhí)行的Action都封裝進(jìn)了該Transition中。

          接下來出個(gè)簡單的數(shù)學(xué)題,假設(shè)有3個(gè)狀態(tài),4個(gè)事件,最多需要有多少個(gè)Transition實(shí)例呢?

          State Machine

          為了保證線程安全,我們將使用三個(gè)隊(duì)列,同時(shí)和上述狀態(tài)機(jī)一致,我們將設(shè)定一個(gè)初始化狀態(tài):

          class StateMachine<StateEvent{
              public var currentState: State {
                  return {
                      workingQueue.sync {
                          return internalCurrentState
                      }
                  }()
              }
              private var internalCurrentState: State
              
              private let lockQueue: DispatchQueue
              private let workingQueue: DispatchQueue
              private let callbackQueue: DispatchQueue
              
              public init(initialState: State, callbackQueue: DispatchQueue? = nil) {
                  self.internalCurrentState = initialState
                  self.lockQueue = DispatchQueue(label: "com.statemachine.queue.lock")
                  self.workingQueue = DispatchQueue(label: "com.statemachine.queue.working")
                  self.callbackQueue = callbackQueue ?? .main
              }
          }

          處理transition事件默認(rèn)是在主隊(duì)列中,同時(shí)也可以自定義一個(gè)隊(duì)列并在初始化時(shí)傳入,即callbackQueue。

          接下來要做的就是注冊狀態(tài)表了,這里我們使用一個(gè)字典:

           private var transitionsByEvent: [Event : [Transition<StateEvent>]] = [:]

          為什么這里用一個(gè)字典呢?

          這里應(yīng)該很好理解,因?yàn)闋顟B(tài)機(jī)接受Event,然后結(jié)合當(dāng)前State 以及 Event 去查詢有沒有對應(yīng)的Transition,如果有就進(jìn)行相關(guān)處理,如果沒有就不執(zhí)行。同時(shí)因?yàn)槊恳粋€(gè)Event,對應(yīng)的都是一組Transition(因?yàn)槊恳粋€(gè)狀態(tài)都可以接受該Event轉(zhuǎn)換為另一個(gè)狀態(tài)),那么這個(gè)字典的Value值很自然的就應(yīng)該是一個(gè)Transition數(shù)組了。

          注冊Transition
          public func add(transition: Transition<State, Event>) {
              lockQueue.sync {
                  if let transitions = self.transitionsByEvent[transition.event] {
                      if (transitions.filter { return $0.source == transition.source }.count > 0) {
                          assertionFailure("Transition with event '\(transition.event)' and source '\(transition.source)' already existing.")
                      }
                      self.transitionsByEvent[transition.event]?.append(transition)
                  } else {
                      self.transitionsByEvent[transition.event] = [transition]
                  }
              }
          }

          很簡單,使用串行隊(duì)列實(shí)現(xiàn)一個(gè)讀寫鎖,保證線程安全。

          接受Event

          當(dāng)注冊完?duì)顟B(tài)表之后,剩下的就是接受相應(yīng)Event,然后做出相應(yīng)的處理了。要注意的一點(diǎn)就是類似前面OOP中調(diào)用的exit 方法 以及 enter 方法,初始化transition的時(shí)候也有相應(yīng)的preAction和postAction, 調(diào)用方法的時(shí)候需要注意順序。

              /// 接受事件,做出相應(yīng)處理
              /// - Parameters:
              ///   - event: 觸發(fā)的事件
              ///   - execution: 狀態(tài)轉(zhuǎn)移的過程中需要執(zhí)行的 操作
              ///   - callBack: 狀態(tài)轉(zhuǎn)移的回調(diào)
              public func process(event: Event, execution: (() -> Void)? = nil, callback: TransitionBlock? = nil) {
                  var transitions: [Transition<StateEvent>]?
                  lockQueue.sync {
                      transitions = self.transitionByEvent[event]
                  }
                  
                  workingQueue.async {
                      let performableTransitions = transitions?.filter { $0.source == self.internalCurrentState } ?? []
                      
                      if performableTransitions.count == 0 {
                          self.callbackQueue.async { callback?(.failure) }
                          return
                      }
                      
                      assert(performableTransitions.count == 1"Found multiple transitions with event '\(event)' and source '\(self.internalCurrentState)'.")
                      
                      let transition = performableTransitions.first!
                      
                      self.callbackQueue.async {
                          transition.executePreAction()
                      }
                      
                      self.callbackQueue.async {
                          execution?()
                      }
                      
                      self.internalCurrentState = transition.destination
                      
                      self.callbackQueue.async {
                          transition.executePostAction()
                      }
                      
                      self.callbackQueue.async {
                          callback?(.success)
                      }
                  }
              }

          這里接受Event觸發(fā)之后,首先當(dāng)然是判斷該事件對應(yīng)的Transition是否注冊,如果注冊失敗,那就執(zhí)行失敗的callback,反之按照以下順序執(zhí)行:

          1、先執(zhí)行該Transition中的preAction 

          2、然后是Process方法中傳入的執(zhí)行操作Block 

          3、接著改變狀態(tài)機(jī)當(dāng)前狀態(tài) 

          4、然后是執(zhí)行該Transition中的postAction 5、最后是執(zhí)行成功的回調(diào)

          當(dāng)然這里有一個(gè)要注意的地方,就是callbackQueue默認(rèn)為主隊(duì)列,如果是初始化時(shí)傳入自定義隊(duì)列的話,最好是串行隊(duì)列!因?yàn)檫@樣可以確保preAction, postAction, callBack等等可以按照順序執(zhí)行。

          如何使用呢?

          還是上述那個(gè)簡單的例子,但是這里我們直接定義事件和狀態(tài)的枚舉即可:

          enum EventType {
              case clickWalkButton
              case clickRunButton
          }

          enum StateType {
              case walk
              case run
          }

          typealias StateMachineDefault = StateMachine<StateTypeEventType>
          typealias TransitionDefault = Transition<StateTypeEventType>

          然后就是初始化狀態(tài)機(jī)了:

          func initialStateMachine() {
              stateMachine = StateMachineDefault.init(initialState: .walk)

              let a = TransitionDefault.init(with: .clickWalkButton, from: .run, to: .walk) {
                  NSLog("準(zhǔn)備走")
              } postBlock: {
                  NSLog("開始走了")
              }

              let b = TransitionDefault.init(with: .clickRunButton, from: .walk, to: .run) {
                  NSLog("準(zhǔn)備跑")
              } postBlock: {
                  NSLog("開始跑了")
              }

              stateMachine.add(transition: a)
              stateMachine.add(transition: b)
          }

          最后就是直接傳入事件觸發(fā)即可:

          stateMachine.process(event: .clickRunButton)

          實(shí)際案例

          在實(shí)際的開發(fā)中,我們經(jīng)常會遇到鍵盤的各種狀態(tài)切換的場景,這種場景其實(shí)就很適合使用狀態(tài)機(jī),因?yàn)橐坏┏^三種狀態(tài),使用狀態(tài)機(jī)就會非常高效。

          首先確定要鍵盤點(diǎn)擊的幾個(gè)狀態(tài):

          /// 鍵盤狀態(tài)
          enum KeyboardStateState {
              case prepareToEditText   // 準(zhǔn)備編輯
              case prepareToRecord     // 準(zhǔn)備錄音
              case editingText         // 正在編輯
              case showingPannel       // 正在展示pannel
          }

          然后是鍵盤點(diǎn)擊的觸發(fā)事件:

          /// 觸發(fā)事件
          enum KeyboardEventEvent {
              case clickSwitchButton    // 點(diǎn)擊切換按鈕
              case clickMoreButton      // 點(diǎn)擊more按鈕
              case tapTextField         // 點(diǎn)擊輸入框
              case vcDidTapped          // 點(diǎn)擊控制器空白區(qū)域
          }

          那么這個(gè)時(shí)候,在觸發(fā)事件和鍵盤狀態(tài)之間就出現(xiàn)了一個(gè)關(guān)聯(lián)關(guān)系,我們可以UML化這種關(guān)聯(lián)

          觸發(fā)事件\初始狀態(tài) prepareToEdit prepareToRecord editingText showingPannel
          clickSwitchButton prepareToRecord editingText prepareToRecord prepareToRecord
          clickMoreButton showingPannel showingPannel showingPannel prepareToEdit
          tapTextField editingText / / editingText
          vcDidTapped / / prepareToEdit prepareToEdit

          接下來就可以將不同狀態(tài)之間改變的事件添加入狀態(tài)機(jī)中:

          stateMachine.listen(.clickSwitchButton, 
                              transform: .prepareToEditText, 
                              toState: .prepareToRecord) { [weak self] (transition) in

          }

          stateMachine.listen(.clickSwitchButton, 
                              transform: .prepareToRecord, 
                              toState: .editingText) { [weak self] (transition) in

          }
          ......

          最后就是觸發(fā)了,當(dāng)點(diǎn)擊按鈕等交互時(shí),即可調(diào)用該觸發(fā)條件:

              //MARK: -
              //MARK: 點(diǎn)擊事件
              @objc func switchButtonClicked(_ sender: UIButton) {
                  stateMachine.trigger(.clickSwitchButton)
              }
              
              @objc func morefeaturesButtonClicked(_ sender: UIButton) {
                  stateMachine.trigger(.clickMoreButton)
              }

          這樣一個(gè)鍵盤的切換事件就搞定了。這種方式比大量的判斷代碼根據(jù)可讀性,而且可拓展性也更強(qiáng),如果后續(xù)想要再增加一種狀態(tài)的話,那無非就是狀態(tài)機(jī)狀態(tài)多添加幾種狀態(tài)轉(zhuǎn)移的條件而已,這并不復(fù)雜。

          終章

          好了,狀態(tài)機(jī)介紹完了。那為什么要寫一篇文章來介紹狀態(tài)機(jī)呢?主要原因是因?yàn)槲矣X得它在管理UI的多種狀態(tài)時(shí)非常高效,特別是當(dāng)狀態(tài)數(shù)大于三個(gè)以上的時(shí)候,比如管理IM模塊中鍵盤的各種狀態(tài)時(shí)。

          而如果是僅僅通過枚舉來管理多種狀態(tài),那代碼中就會有大量的當(dāng)前狀態(tài)判斷的代碼,很不易維護(hù);如果要添加一個(gè)新的狀態(tài),那么原有狀態(tài)判斷的代碼需要大量的修改,不滿足開閉原則(對修改關(guān)閉,對拓展開放)等等。

          最后希望大家在合適的場景中使用狀態(tài)機(jī)吧。

          引用

          [1] https://web.archive.org/web/20140327131120/http://www4.ncsu.edu/~drwrigh3/docs/courses/csc216/fsm-notes.pdf

          [2] https://www.conradbock.org/statemachine.html

          [3] https://en.wikipedia.org/wiki/Finite-state_machine

          [4] http://www.ios5.online/ios/ioskf/ioskfajc/201703/50096.html

          [5] https://faramira.com/implementing-a-finite-state-machine-using-c-in-unity-part-1

          [6] https://www.youtube.com/watch?v=Qa6csfkK7_I


          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 103
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄a片免费观看 | 黄色av网站在线观看 | 一级黄色视频在线观看 | 成人做爰黄 片免费观看 | 欧美日韩性爱视频一区二区 |