Swift36/90Days - 状态可变的不可变类型

Swift90Days - 状态可变的不可变类型

背景

摘录了一部分原文的内容记录一下,观点未必彻底正确,可是有参考的必要。git

简介

函数式编程中有个很重要的概念:不可变。可是在实际的开发过程当中,又经常为这三个字所困扰:对象的状态总归是常常要变化的,如何才能既可变又不可变?github

在函数式编程里,对于状态的改变,咱们能够简单的返回一个新的对象,而不是修改对象的状态。编程

对比

不妨先看一看 mutating 的方式:swift

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 框架里,“不可变”无疑是充满挑战的,并且彻底的“不可变”也是不可能的。函数

即便如此,我以为从这件事情中也受益不少,至少引导我进行了一些良心的思考。这便足够了。单元测试


参考文档:测试

相关文章
相关标签/搜索