[Swift] 프로토콜 - 2 ( Protocol )

위임 (Delegation)

델리게이션은 디자인 패턴중 하나로 클래스 혹은 구조체 타입이 책임의 일부를 다른 타입의 인스턴스로 양도(위임)할 수 있는 디자인 패턴이다.

예제를 통하여 보자

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

protocol DiceGameDelegate: AnyObject {
    func gameDidStart(_ game: DiceGame)
    func gameStart(_ game: DiceGame, didStartNewTrunWithDiceroll: Int)
    func gmaeDidEnd(_ game: DiceGame)
}
  • DiceGame 프로토콜은 Dice를 포함하는 모든 게임에 채택할 수 있는 프로토콜이다
  • DiceGameDelegatate 프로토콜은 DiceGame의 진행사항을 추적하는 프로토콜이다.

이 프로토콜을 타입으로 가지는 delegate는 참조 사이클의 방지를 위해 weak으로 선언해야 할 것

AnyObject를 상속함으로써 클래스만이 채택할 수 있는 프로토콜이란 것을 알 수 있다.

class SnakesAndLadders: DiceGame {
    let finalSquare = 25
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
    var square = 0
    var board: [Int]
    init() {
        board = Array(repeating: 0, count: finalSquare + 1)
        board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
        board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
    }
    weak 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)
    }
}

SnakesAndLadders는 DiceGame 프로토콜을 채택하였기 때문에 저장 프로퍼티 dice와 메서드 play()를 구현해야 함

  • dice : Dice() 인스턴스를 초기값으로 가짐
  • play() : 게임을 시작하고 DiceGameDelegate 타입의 변수 delegate를 사용하여 게임이 시작되거나 종료되었을 때 해당 인스턴스의 메서드를 호출
  • delegate: 게임을 하는 데 있어 필수적인 요소는 아니기 때문에 옵셔널로 선언 옵셔널값이기 때문에 nil값을 초기값으로 가진다. nil값을 가질 경우 옵셔널 체인으로 인해 메서드 호출을 하지 못함

DiceGameDelegate의 메서드인 gmaDidStart, game, gameDidEnd 메서드를 구현한 곳이 안보인다.

해당 메서드를 구현해보자

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")
    }
}

DiceGameDelegate 프로토콜을 채택한 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

SnakesAndLadders의 인스턴스 game은 DiceGameTracker의 인스턴스 tracker를 delegate 프로퍼티로 할당 받는다.

이후 play()메서드를 실행할 때 play()메서드 안에서 실행되는 DiceGameDelegate 메서드들은

DiceGameTracker 인스턴스인 delegate를 통해 호출 될것이다.

앞서 델리게이트란 클래스 혹은 구조체 타입이 책임의 일부를 다른 타입의 인스턴스로 양도(위임) 하는 것 이라고

하였다.

여기서 SnakesAndLadder 타입은 DiceGame 프로토콜을 채택하여 dice와 play를 구현하였다

SnakesAndLadder 타입은 말한다

play 메서드는 게임을 실행할거야.

만약 너가 게임의 진행사항을 알고 싶다면 중간 중간 delegate 프로퍼티를 통해 알려줄게.

너가 할일은 DiceGameDelegate 프로토콜을 채택한 타입을 만들어

DiceGameDelegate 프로토콜은 3가지 메서드가 있어

게임이 시작했을 때 뭘 하고 싶은지 gameDidStart ()

게임 진행 중에 뭘 하고 싶은지 game()

게임이 끝났을 때 뭘 하고 싶은지 gameDidEnd()

3가지를 구현한 타입의 인스턴스를 나의 delegate 변수에 할당하기만 하면 되

SnakesAndLadder 타입의 인스턴스가 게임의 시작, 끝 그리고 중간에 뭘 할 것인지에 대해

DiceGameTracker 타입의 인스턴스에게 위임 하는 것. 그것이 델리게이트다

댓글남기기