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