Swift 結構體什麼時候使用 mutating 函數

做者:Natasha,原文連接,原文日期:2016/01/13
譯者:bestswifter;校對:saitjr;定稿:千葉知風編程

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:code

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)
    }
}

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

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 方法就好了。

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg。��法是否知足預期。若是你以爲接口寫起來很怪異、不直觀,那就去換一種方式去實現吧!最後別忘了用私有 setter 設置你的內部變量哦!
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg

相關文章
相關標籤/搜索