最近在学习 SwiftUI ,写了一个计算器的 Demo。写完后学习并理解到一种代码设计,感受颇有收获,在此记录一下。git
咱们将 App 看成一个状态机,状态机的状态决定 App 的界面。首先咱们理出 App 都有什么状态?每一个状态须要什么样的条件会触发什么样的 Action?Action 执行后状态机会进入什么样的次态?理清这些后,根据设计,随着状态机状态的改变,,App 的界面天然会跟着更新。github
在这里我用我写的计算器 Demo 为例讲解下这种方式。swift
计算器的业务逻辑就是接受用户的输入的算式,而后计算并输出算式的结果。那么计算器的状态就是由算式的状态来决定。markdown
首先看看算式的组成:app
“左侧数字 + 计算符号 + 右侧数字 + 计算符号或者等号”oop
看着算式的结构,咱们就能捋出计算器都有什么状态:学习
因此咱们须要在代码中定义出五个状态:编码
// 这五个枚举和上面定义的状态按顺序一一对应 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
这里主要以状态 1(left)为例,讲解在状态 1 下遇到什么条件会触发什么样的 Action,而后会进入哪一个状态。设计
left
,只不过这个状态下的数据会发生改变语言是苍白的,直接看代码:
// 因为代码过长,所有附上会影响篇幅,此处仅附上部分代码。详细代码能够在文末找到连接 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,如何你也对这个感兴趣,那么我推荐这本我在用的教材:
本文仅为我的意见,不构成编码建议。