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 框架裏,「不可變」無疑是充滿挑戰的,並且徹底的「不可變」也是不可能的。函數

即便如此,我以爲從這件事情中也受益不少,至少引導我進行了一些良心的思考。這便足夠了。單元測試


參考文檔:測試

相關文章
相關標籤/搜索