有限状态自动机(FSM “finite state machine” 或者FSA “finite state automaton” )是为研究有限内存的计算过程和某些语言类而抽象出的一种计算模型。有限状态自动机拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。有限状态自动机可以表示为一个有向图。有限状态自动机是自动机理论的研究对象。
我们来看以下片段代码:1
2
3
4
5
6
7class ViewController {
var tableView: UITableView!
var errorLabel: UILabel!
var emptyStateLabel: UILabel!
var activityIndicator: UIActivityIndicatorView!
// Some implementation
}
我们得出4种状态,且每种独立存在:
- 显示数据
- 是否在加载数据中
- 是否显示错误
- 空状态
我们在控制器中定义该枚举:我们在控制器中加载数据的过程,状态的变化:1
2
3
4
5
6
7
8extension 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
63protocol ItemService {
func loadItems(completion: @escaping (Result<[Item]>) -> Void)
}
class ViewController: UIViewController {
var tableView: UITableView!
var errorLabel: UILabel!
var emptyStateLabel: UILabel!
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()为标记且响应每一个状态进入后所响应执行的业务变化。分别实现不同状态的子类,并且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
39class 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)
}
}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
38
39
40
41
42
43
44
45
46
47
48final 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
}
}这样对于状态机状态发生变化的时候,self.state.enter()方法响应状态变更后发生的事情,这样代码是不是很简洁?haha。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
37class ViewController: UIViewController {
var tableView: UITableView!
var errorLabel: UILabel!
var emptyStateLabel: UILabel!
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()
}
}
}