有限状态自动机(FSM “finite state machine” 或者FSA “finite state automaton” )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。
我们来看以下片段代码:

1
2
3
4
5
6
7
class ViewController {
@IBOutlet var tableView: UITableView!
@IBOutlet var errorLabel: UILabel!
@IBOutlet var emptyStateLabel: UILabel!
@IBOutlet var activityIndicator: UIActivityIndicatorView!
// Some implementation
}

我们得出4种状态,且每种独立存在:

  • 显示数据
  • 是否在加载数据中
  • 是否显示错误
  • 空状态
    我们在控制器中定义该枚举:
    1
    2
    3
    4
    5
    6
    7
    8
    extension ViewController {
    enum State {
    case loading
    case showingData([Item])
    case empty
    case error(Error)
    }
    }
    我们在控制器中加载数据的过程,状态的变化:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    protocol ItemService {
    func loadItems(completion: @escaping (Result<[Item]>) -> Void)
    }

    class ViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    @IBOutlet var errorLabel: UILabel!
    @IBOutlet var emptyStateLabel: UILabel!
    @IBOutlet var activityIndicator: UIActivityIndicatorView!

    private var items: [Item] = []

    var itemService: ItemService!

    private var state: State = .empty {
    didSet {
    hideAll()

    switch state {
    case .empty:
    emptyStateLabel.isHidden = false
    case .error(let error):
    errorLabel.isHidden = false
    errorLabel.text = error.localizedDescription
    case .loading:
    activityIndicator.isHidden = false
    activityIndicator.startAnimating()
    case .showingData(let items):
    self.items = items
    tableView.isHidden = false
    tableView.reloadData()
    }
    }
    }

    override func viewDidLoad() {
    super.viewDidLoad()
    loadData()
    }

    private func loadData() {
    state = .loading

    itemService.loadItems { [weak self] result in
    switch result {
    case .success(let items) where items.isEmpty:
    self?.state = .empty
    case .success(let items):
    self?.state = .showingData(items)
    case .failure(let error):
    self?.state = .error(error)
    }
    }
    }

    private func hideAll() {
    tableView.isHidden = true
    errorLabel.isHidden = true
    activityIndicator.isHidden = true
    activityIndicator.stopAnimating()
    emptyStateLabel.isHidden = true
    }
    }

    用状态设计模式实现有限状态机

    用状态设计模式来改进viewcontroller 中的状态机: 为每个状态独立一个子类,分别控制状态对应的响应变化。 首先应该定义一个State根class, 提供一个静态工厂方法,构建当前传递进来的枚举状态构建所对应的状态子类。 状态方法enter()为标记且响应每一个状态进入后所响应执行的业务变化。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    class State {

    weak var viewController: ViewController!

    init(viewController: ViewController) {
    self.viewController = viewController
    }

    static func state(_ state: Kind, viewController: ViewController) -> State {
    switch state {
    case .showingData(let items):
    return ShowingDataState(items: items, viewController: viewController)
    case .loading:
    return LoadingState(viewController: viewController)
    case .empty:
    return EmptyState(viewController: viewController)
    case .error(let error):
    return ErrorState(error: error, viewController: viewController)
    }
    }

    func enter() {
    viewController.tableView.isHidden = true
    viewController.errorLabel.isHidden = true
    viewController.activityIndicator.isHidden = true
    viewController.activityIndicator.stopAnimating()
    viewController.emptyStateLabel.isHidden = true
    }
    }

    extension State {

    enum Kind {
    case loading
    case showingData([Item])
    case empty
    case error(Error)
    }
    }
    分别实现不同状态的子类,并且final修饰,保证子类不可再继承。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    final class ShowingDataState: State {

    let items: [Item]

    init(items: [Item], viewController: ViewController) {
    self.items = items
    super.init(viewController: viewController)
    }

    override func enter() {
    super.enter()
    viewController.items = items
    viewController.tableView.isHidden = false
    viewController.tableView.reloadData()
    }
    }

    final class LoadingState: State {

    override func enter() {
    super.enter()
    viewController.emptyStateLabel.isHidden = false
    }
    }

    final class EmptyState: State {

    override func enter() {
    super.enter()
    viewController.emptyStateLabel.isHidden = false
    }
    }

    final class ErrorState: State {

    let error: Error

    init(error: Error, viewController: ViewController) {
    self.error = error
    super.init(viewController: viewController)
    }

    override func enter() {
    super.enter()
    viewController.errorLabel.isHidden = false
    viewController.errorLabel.text = error.localizedDescription
    }
    }
    OK,接下来我们看一些viewController如何使用有限状态机:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class ViewController: UIViewController {
    @IBOutlet var tableView: UITableView!
    @IBOutlet var errorLabel: UILabel!
    @IBOutlet var emptyStateLabel: UILabel!
    @IBOutlet var activityIndicator: UIActivityIndicatorView!

    var items: [Item] = []

    var itemService: ItemService!

    lazy var state = State.state(.empty, viewController: self)

    override func viewDidLoad() {
    super.viewDidLoad()
    loadData()
    }

    private func loadData() {
    state = .state(.loading, viewController: self)
    state.enter()

    itemService.loadItems { [weak self] result in
    guard let self = self else { return }

    switch result {
    case .success(let items) where items.isEmpty:
    self.state = .state(.empty, viewController: self)
    case .success(let items):
    self.state = .state(.showingData(items), viewController: self)
    case .failure(let error):
    self.state = .state(.error(error), viewController: self)
    }

    self.state.enter()
    }
    }
    }
    这样对于状态机状态发生变化的时候,self.state.enter()方法响应状态变更后发生的事情,这样代码是不是很简洁?haha。

评论