타입으로 프로토콜(protocol as type)
- 프로토콜 자체는 어떠한 기능도 구현하지 않는다. 그럼에도 불구하고 완전한 타입으로서 사용 가능하다. 타입으로서 프로토콜을 사용하는 것은 존재타입(existential type)이라고 한다.
- 함수, 메서드 또는 초기화 구문에서 파라미터 타입 또는 반환타입
- 상수, 변수 또는 프로퍼티 타입
- 배열, 딕셔너리 또는 다른 컬렉션 항목의 타입
class Dice
let sides : Int
let generator : RandomNumberGenerator
init(sides : Int, generator : RandomNumberGenerator) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(gererator.random() * Double(sides)) + 1
}
}
var d6 = Dice(sides : 6, generator : LinearCongruentialGenerator())
for _ in 1...5 {
print("RandomDice roll! \(d6.roll)")
}
- 위임(Delegation)은 클래스 또는 구조체가 책임의 일부를 다른 타입의 인스턴스에 넘겨주거나 위임할 수 있도록 하는 디자인 패턴이다. 이 패턴은 위임된 기능을 제공하기 위해 준수하는 타입이 보장되도록 위임된 책임을 캡슐화하는 프로토콜을 정의하여 구현한다.
protocol DiceGame {
var dice : Dice {get}
func play()
}
protocol DiceGameDelegate : AnyObject {
func gameDidStart(_ game : DiceGame)
func game(_ game : DiceGame, didStartNewTurnWithDiceRoll diceRoll : Int)
func gameDidEnd(_ game : DiceGame)
}
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
}
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)
}
}
확장을 통한 프로토콜 준수성 추가
- 기존 타입에 대해 새로운 프로토콜을 채택하고 준수하기 위해서 기존 타입을 확장할 수 있다. 확장은 기존 타입에 프로퍼티, 메서드, 서브 스크립트의 추가가 가능하므로 프로토콜이 요구하는 모든 요구사항을 추가할 수 있다.
protocol TextRepresentable {
var textualDescription : String {get}
}
extension Dice : TextRepresentable {
var textualDescription : String {
return "A \(sides) -sided dice"
}
}
let d12 = Dice(sides : 12, generator : LinearCongruentialGenerator())
print(d12.textualDescription)
- 타입을 확장할 때 제약조건을 나열해서 일반 타입이 프로토콜을 조건적으로 준수하도록 만들 수 있다. 일반적 where절을 작성해서 제약조건을 명시한다.
extension Array: TextRepresentable where Element : TextRepresentable {
var textualDescription : String {
let itemsAsText = self.map { $0.textualDescription }
return "[" + itemAsText.joined(separator: ",") + "]"
}
}
- 확장과 함께 프로토콜 채택 선언은 프로토콜을 늦게나마 명시하는 경우 비어있는 확장을 통해서 프로토콜 채택이 가능하다.
struct Hamster {
var mane : String
var textualDescription : String {
return "A hamster \(name)"
}
}
extension Hamster : TextRepresentable {}