位置:首頁 > 高級語言 > Swift教學 > Swift委托(代理)模式

Swift委托(代理)模式

委托(代理)模式

委托是一種設計模式,它允許類或結構體將一些需要它們負責的功能交由(委托)給其他的類型。

委托模式的實現很簡單: 定義協議封裝那些需要被委托的函數和方法, 使其遵循者擁有這些被委托的函數和方法

委托模式可以用來響應特定的動作或接收外部數據源提供的數據,而無需要知道外部數據源的類型。

下文是兩個基於骰子遊戲的協議:

protocol DiceGame {
    var dice: Dice { get }
    func play()
}

protocol DiceGameDelegate {
    func gameDidStart(game: DiceGame)
    func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll:Int)
    func gameDidEnd(game: DiceGame)
}

DiceGame協議可以在任意含有骰子的遊戲中實現,DiceGameDelegate協議可以用來追蹤DiceGame的遊戲過程。

如下所示,SnakesAndLaddersSnakes and Ladders(譯者注:控製流章節有該遊戲的詳細介紹)遊戲的新版本。新版本使用Dice作為骰子,並且實現了DiceGameDiceGameDelegate協議

class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dic = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
    var board: Int[]
    init() {
        board = Int[](count: finalSquare + 1, repeatedValue: 0)
        board[03] = +08; board[06] = +11; borad[09] = +09; board[10] = +02
        borad[14] = -10; board[19] = -11; borad[22] = -02; board[24] = -08
    }
     var delegate: DiceGameDelegate?
     func play() {
         square = 0
         delegate?.gameDidStart(self)
         gameLoop: while square != finalSquare {
             let diceRoll = dice.roll()
             delegate?.game(self,didStartNewTurnWithDiceRoll: diceRoll)
             switch square + diceRoll {
             case finalSquare:
                 break gameLoop
             case let newSquare where newSquare > finalSquare:
                 continue gameLoop
             default:
             square += diceRoll
             square += board[square]
             }
         }
         delegate?.gameDIdEnd(self)
     }
}

遊戲的初始化設置(setup)SnakesAndLadders類的構造器(initializer)實現。所有的遊戲邏輯被轉移到了play方法中。


注意: 因為delegate並不是該遊戲的必備條件,delegate被定義為遵循DiceGameDelegate協議的可選屬性。
 

DicegameDelegate協議提供了三個方法用來追蹤遊戲過程。被放置於遊戲的邏輯中,即play()方法內。分彆在遊戲開始時,新一輪開始時,遊戲結束時被調用。

因為delegate是一個遵循DiceGameDelegate的可選屬性,因此在play()方法中使用了可選鏈來調用委托方法。 若delegate屬性為nil, 則委托調用優雅地失效。若delegate不為nil,則委托方法被調用

如下所示,DiceGameTracker遵循了DiceGameDelegate協議

class DiceGameTracker: DiceGameDelegate {
    var numberOfTurns = 0
    func gameDidStart(game: DiceGame) {
        numberOfTurns = 0
        if game is SnakesAndLadders {
            println("Started a new game of Snakes and Ladders")
        }
        println("The game is using a \(game.dice.sides)-sided dice")
    }
    func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
        ++numberOfTurns
        println("Rolled a \(diceRoll)")
    }
    func gameDidEnd(game: DiceGame) {
        println("The game lasted for \(numberOfTurns) turns")
    }
}

DiceGameTracker實現了DiceGameDelegate協議的方法要求,用來記錄遊戲已經進行的輪數。 當遊戲開始時,numberOfTurns屬性被賦值為0;在每新一輪中遞加;遊戲結束後,輸出打印遊戲的總輪數。

gameDidStart方法從game參數獲取遊戲信息並輸出。game在方法中被當做DiceGame類型而不是SnakeAndLadders類型,所以方法中隻能訪問DiceGame協議中的成員。

DiceGameTracker的運行情況,如下所示:

let tracker = DiceGameTracker()
let game = SnakesAndLadders()
game.delegate = tracker
game.play()
// Started a new game of Snakes and Ladders
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// The game lasted for 4 turns