デリゲーションは、クラスや構造体が別の型のインスタンスに責務を渡す(デリゲートする)ことができるようにするデザインパターンです。このデザインパターンは、準拠する型がデリゲートされた機能を提供することを保証するよう、デリゲートされる責務についてのプロトコルを定義して実装します。特定のアクションに対応させるために、あるいは外部のソースの型について知ることなくそのソースからデータを取得するために、デリゲーションを使用することができます。

次の例では、サイコロベースのボードゲームで使用する 2 つのプロトコルを定義しています。

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 の進行を型が追跡するために採用することができます。

次は、Control Flow で紹介した蛇と梯子のゲームです。このバージョンでは、サイコロを振るのに Dice インスタンスを使用し、DiceGame プロトコルを採用し、進行について DiceGameDelegate に通知しています。

class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = 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; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[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)
    }
}

蛇と梯子ゲームの説明は、Control Flow の Break を確認してください。

ゲームのこのバージョンは、DiceGame プロトコルを採用する SnakesAndLadders クラスとしてまとめられています。プロトコルに準拠するために、gettable な dice プロパティと play() メソッドがあります。(dice プロパティは、初期化後に変更する必要が無いのと、プロトコルが gettable を要件としているため、定数プロパティとして宣言されています。)

蛇と梯子ゲームのボードのセットアップは、クラスの init() イニシャライザ内で行われています。ゲームロジックはすべてプロトコルの play メソッド内に移動されており、サイコロの出目を得るために、プロトコル要件の dice プロパティを使用しています。

ゲームを進めるためにデリゲートが必要ということではないため、delegate プロパティはオプショナルの DiceGameDelegate として定義されていることに注目してください。オプショナル型であるため、delegate プロパティは自動的に初期値の nil に設定されます。後でプロパティに適切なデリゲートを設定することができます。

DiceGameDelegate には、ゲームの進行を追跡する 3 つのメソッドがあります。これらのメソッドは、play() メソッド内のゲームロジックに組み込まれていて、ゲーム開始時、新しいターンの開始時、そしてゲーム終了時に呼び出されます。

delegate プロパティはオプショナルの DiceGameDelegate であるため、play() メソッドはデリゲートのメソッドを呼び出すたびにオプショナルチェーンを使用しています。delegate プロパティが nil の場合、デリゲートの呼び出しはエラーにならずに失敗します。delegate プロパティが nil でない場合には、デリゲートメソッドが呼び出され、パラメータとして SnakesAndLadders インスタンスが渡されます。

次の例は、DiceGameDelegate プロトコルを採用する DiceGameTracker クラスを示しています。

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

DiceGameTracker は、DiceGameDelegate の要件である 3 つすべてのメソッドを実装しています。ゲームのターン数を記録するために、これらのメソッドを使用しています。ゲーム開始時に numberOfTurns プロパティをゼロにリセットし、新しいターンの開始時に増加させ、ゲーム終了時に合計ターン数を出力します。

gameDidStart の実装では、game パラメータを使用してプレイ中のゲームについての紹介を出力します。game パラメータは DiceGame 型で、SnakesAndLadders 型ではないため、gameDidStart では DiceGame プロトコルでの一部として実装されたメソッドとプロパティにのみアクセスして使用することができます。しかしながら、型キャストを使用してインスタンスのそもそもの型を問い合わせることができます。この例では、game が実際には SnakesAndLadders のインスタンスかを確認し、そうである場合には適切なメッセージを出力します。

また、gameDidStart は渡された game パラメータの dice プロパティにアクセスします。game は DiceGame プロトコルに準拠していることがわかっているため、dice プロパティがあることが保証されており、どのような種類のゲームをしているかにかかわらず、gameDidStart(_:) メソッドがサイコロの sides プロパティにアクセスして出力することができます。

次は、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

Portions of this page are translations based on work created and shared by Apple and used according to terms described in the Creative Commons Attribution 4.0 International License.