Swift90Days - 状态可变的不可变类型
背景
摘录了一部分原文的内容记录一下,观点未必完全正确,但是有参考的必要。
简介
函数式编程中有个很重要的概念:不可变。但是在实际的开发过程中,又常常为这三个字所困扰:对象的状态总归是经常要变化的,如何才能既可变又不可变?
在函数式编程里,对于状态的改变,我们可以简单的返回一个新的对象,而不是修改对象的状态。
对比
不妨先看一看 mutating
的方式:
class Scorekeeper {
var runningScore: Int
init (score: Int = 0) {
self.runningScore = score
}
func incrementScoreBy(points: Int) {
runningScore += points
}
}
let scoreKeeper = Scorekeeper()
scoreKeeper.incrementScoreBy(5)
println(scoreKeeper.runningScore)
// prints 5
再看一下 immutable
的方式:
class Scorekeeper {
let runningScore: Int
init (score: Int = 0) {
self.runningScore = score
}
func incrementScoreBy(points: Int) -> Scorekeeper {
return Scorekeeper(score: self.runningScore + points)
}
}
let scorekeeper = Scorekeeper()
let scorekeeperWithIncreasedScore = scorekeeper.incrementScoreBy(5)
println(scorekeeperWithIncreasedScore.runningScore)
观察
对比一下上面的两个例子:
- 第一个例子使用
var
定义Scorekeeper
实例对象,因为它必须是可变的。 - 第二个例子使用
let
定于Scorekeeper
实例对象,因为这个对象没有任何变化。 - 第一个例子容易产生有趣而不可预知的副作用。如果多个外部对象持有了
scorekeeper
这个实例,那么现在有两种方式改变runningScore
:一种是重新给runningScore
赋值,另一种是调用incrementScoreBy()
这个方法。不管是哪一种方法,因为状态是可编辑的,所以都有可能会导致不可预知的问题。 - 第二个例子则不会有无法预知的问题,因为
runningScore
是无法直接编辑的 (它是常量) 。而incrementScoreBy()
返回的是一个全新的变量,所以所有的持有scorekeeper
的外部对象在访问的时候都能获取到理想的结果。 - 第一个例子的
incrementScoreBy()
方法没有返回值。想象一下如果我要写个单元测试,第一眼看过去很容易不知所措。 - 第二个例子的
incrementScoreBy()
返回一个新的对象,单元测试对我来说就清晰多了。
结论
避免直接的状态变化让我受益匪浅,在现有的 iOS 框架里,“不可变”无疑是充满挑战的,而且完全的“不可变”也是不可能的。
即使如此,我觉得从这件事情中也受益很多,至少引导我进行了一些良心的思考。这便足够了。
参考文档:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。