位置:首页 > 高级语言 > 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