Swift 結構體何時使用 mutating 函數

beckwith 8年前發布 | 6K 次閱讀 Swift 函數式編程 Apple Swift開發

Swift 最棒的特點之一就是它內置了對整體結構的不可變性的支持,這使得我們的代碼更加整潔、安全(關于這個話題,如果還沒看過 這篇文章 ,那么強烈推薦給你)。

不過,真的需要用到可變性時,你應該怎么做呢?

函數式做法

舉個例子,我有一個井字棋棋盤,現在需要改變棋盤上某個位置的狀態:

struct Position {
    let coordinate: Coordinate
    let state: State

    enum State: Int {
        case X, O, Empty
    }
}

struct Board {

    let positions: [Position]

    // 需要添加一個函數來更新這個位置的狀態
    // 狀態從 Empty 改為 X 或者 0
}

如果完全采用 函數式編程的做法 ,你只需要簡單的返回一個新的棋盤即可:

struct Board {

    let positionsMatrix: [[Position]]

    init() {
       // 初始化一個空棋盤的邏輯
    }

    // 函數式編程的做法
    func boardWithNewPosition(position: Position) -> Board {
        var positions = positionsMatrix
        let row = position.coordinate.row.rawValue
        let column = position.coordinate.column.rawValue
        positions[row][column] = position
        return Board(positionsMatrix: positions)
    }
}

我更傾向于使用這種函數式的做法,因為它不會有任何副作用。變量可以繼續保持不可變狀態,當然,這樣也非常易于測試!

class BoardTests: XCTestCase {

    func testBoardWithNewPosition() {
        let board = Board()
        let coordinate = Coordinate(row: .Middle, column: .Middle)

        let initialPosition = board[coordinate]
        XCTAssertEqual(initialPosition.state, Position.State.Empty)

        let newPosition = Position(coordinate: coordinate, state: .X)
        let newBoard = board.boardWithNewPosition(newPosition)
        XCTAssertEqual(newBoard[coordinate], newPosition)
    }
}

不過這種做法并非在所有場景下都是最佳選擇。

使用 Mutating 關鍵字

假設我需要統計每個用戶贏了多少局井字棋,那么我創建了一個 Counter:

struct Counter {
    let count: Int

    init(count: Int = 0) {
        self.count = count
    }

    // 需要實現一個增加計數的方法
}

我依然可以選擇函數式的做法:

struct Counter {
    let count: Int

    init(count: Int = 0) {
        self.count = count
    }

    // 函數式做法
    func counterByIncrementing() -> Counter {
        let newCount = count + 1
        return Counter(count: newCount)
    }
}

不過,如果你真的嘗試了使用這個函數來增加計數,代碼會是這樣:

var counter = Counter()
counter = counter.counterByIncrementing()

這種寫法不夠直觀,可讀性也不高。所以在這種場景下,我更傾向于使用 mutating 關鍵字:

struct Counter {
    // 這個變量現在得聲明成 var
    var count: Int

    init(count: Int = 0) {
        self.count = count
    }

    // 使用 mutating 關鍵字的做法
    mutating func increment() {
        count += 1
    }
}

我不喜歡這個函數帶來的副作用,但是相對于可讀性的提升而言,這樣做是值得的:

var counter = Counter()
counter.increment()

更進一步來說,通過 使用私有 setter 方法 可以確保 count 變量不會被外部修改(因為它現在被聲明為變量了)。這樣,使用變異方法和變量所帶來的負面影響可以被降到最低。

總結

在選擇使用 mutating 關鍵字和函數式編程時,我傾向于后者,但前提是 不會以犧牲可讀性為代價

寫測試是一種很好的檢查接口的方法,它可以判斷你的函數式編程是否真的有意義。如果你覺得代碼比較奇怪而且不夠直觀,那么就換成 mutating 方法吧。只要記得使用變量的私有 setter 方法就行了。

來自: http://swift.gg/2016/06/17/when-to-use-mutating-functions-in-swift-structs/

 

 本文由用戶 beckwith 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!