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
的遊戲過程。
如下所示,SnakesAndLadders
是Snakes and Ladders
(譯者注:控制流章節有該遊戲的詳細介紹)遊戲的新版本。新版本使用Dice
作爲骰子,並且實現了DiceGame
和DiceGameDelegate
協議
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