🔥 프로토콜 준수를 Extension으로 추가하기
Swift에서는 기존 타입을 확장하여 새로운 프로토콜을 채택하고 준수하도록 만들 수 있답니다. 심지어 기존 타입의 소스 코드에 접근할 수 없더라도 말이죠. Extension을 사용하면 기존 타입에 새로운 속성, 메서드, 서브스크립트를 추가할 수 있어서 프로토콜에서 요구하는 사항을 충족시킬 수 있게 됩니다. Extension에 대한 자세한 내용은 Extensions를 참고하시면 좋을 것 같아요.
예를 들어, TextRepresentable
이라는 프로토콜은 텍스트로 표현될 수 있는 모든 타입이 구현할 수 있습니다. 이는 타입 자신에 대한 설명이거나 현재 상태를 텍스트로 나타낸 버전일 수 있죠.
protocol TextRepresentable { var textualDescription: String { get } }
swift
앞서 살펴본 Dice
클래스는 TextRepresentable
을 채택하고 준수하도록 확장할 수 있습니다.
extension Dice: TextRepresentable { var textualDescription: String { return "A \(sides)-sided dice" } }
swift
이 Extension은 Dice
가 원래 구현에서 제공한 것과 정확히 동일한 방식으로 새 프로토콜을 채택합니다. 프로토콜 이름은 타입 이름 뒤에 콜론으로 구분하여 제공되며, 프로토콜의 모든 요구 사항에 대한 구현은 Extension의 중괄호 내에 제공됩니다.
이제 모든 Dice
인스턴스를 TextRepresentable
로 취급할 수 있게 되었네요.
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator()) print(d12.textualDescription) // "A 12-sided dice" 출력
swift
마찬가지로, SnakesAndLadders
게임 클래스도 TextRepresentable
프로토콜을 채택하고 준수하도록 확장할 수 있습니다.
extension SnakesAndLadders: TextRepresentable { var textualDescription: String { return "A game of Snakes and Ladders with \(finalSquare) squares" } } print(game.textualDescription) // "A game of Snakes and Ladders with 25 squares" 출력
swift
프로토콜에 조건부로 준수하기
제네릭 타입은 타입의 제네릭 매개변수가 프로토콜을 준수할 때와 같이 특정 조건에서만 프로토콜의 요구 사항을 충족할 수 있습니다. 타입을 확장할 때 제약 조건을 나열하여 제네릭 타입이 조건부로 프로토콜을 준수하도록 만들 수 있어요. 채택하는 프로토콜의 이름 뒤에 제네릭 where
절을 작성하여 이러한 제약 조건을 작성하면 됩니다. 제네릭 where
절에 대한 자세한 내용은 Generic Where Clauses를 참조하세요.
다음 Extension은 TextRepresentable
을 준수하는 타입의 요소를 저장할 때마다 Array
인스턴스가 TextRepresentable
프로토콜을 준수하도록 만듭니다.
extension Array: TextRepresentable where Element: TextRepresentable { var textualDescription: String { let itemsAsText = self.map { $0.textualDescription } return "[" + itemsAsText.joined(separator: ", ") + "]" } } let myDice = [d6, d12] print(myDice.textualDescription) // "[A 6-sided dice, A 12-sided dice]" 출력
swift
Extension으로 프로토콜 채택 선언하기
만약 어떤 타입이 이미 프로토콜의 모든 요구 사항을 준수하고 있지만, 아직 해당 프로토콜을 채택한다고 명시하지 않았다면, 빈 Extension으로 프로토콜을 채택하도록 만들 수 있습니다.
struct Hamster { var name: String var textualDescription: String { return "A hamster named \(name)" } } extension Hamster: TextRepresentable {}
swift
이제 Hamster
의 인스턴스는 TextRepresentable
이 필요한 곳이라면 어디에서든 사용될 수 있게 되었습니다.
let simonTheHamster = Hamster(name: "Simon") let somethingTextRepresentable: TextRepresentable = simonTheHamster print(somethingTextRepresentable.textualDescription) // "A hamster named Simon" 출력
swift
이렇게 Extension을 활용하면 기존 타입의 기능을 확장하면서도 프로토콜 지향 프로그래밍의 장점을 누릴 수 있게 됩니다. 코드의 재사용성과 유연성이 높아지는 것이죠. 프로토콜과 Extension을 적절히 조합하여 사용한다면 더욱 강력하고 표현력 있는 코드를 작성할 수 있을 거예요.