🔥 프로토콜 조합
때로는 하나의 타입이 여러 프로토콜을 동시에 준수하도록 요구하는 것이 유용할 수 있습니다. 이런 경우 프로토콜 조합
을 사용하여 여러 프로토콜을 하나의 요구사항으로 결합할 수 있습니다. 프로토콜 조합은 조합에 포함된 모든 프로토콜의 요구사항을 결합한 임시 로컬 프로토콜을 정의한 것처럼 동작합니다. 프로토콜 조합 자체는 새로운 프로토콜 타입을 정의하지 않습니다.
프로토콜 조합은 SomeProtocol & AnotherProtocol
과 같은 형태를 가집니다. 필요한 만큼의 프로토콜을 나열할 수 있으며, 앰퍼샌드(&
)로 구분합니다. 프로토콜 목록 외에도 프로토콜 조합에는 하나의 클래스 타입을 포함할 수 있는데, 이를 통해 필수 상위 클래스를 지정할 수 있습니다.
다음은 Named
와 Aged
라는 두 개의 프로토콜을 함수 매개변수의 단일 프로토콜 조합 요구사항으로 결합하는 예제입니다:
protocol Named { var name: String { get } } protocol Aged { var age: Int { get } } struct Person: Named, Aged { var name: String var age: Int } func wishHappyBirthday(to celebrator: Named & Aged) { print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!") } let birthdayPerson = Person(name: "Malcolm", age: 21) wishHappyBirthday(to: birthdayPerson) // "Happy birthday, Malcolm, you're 21!"이 출력됩니다.
swift
이 예제에서 Named
프로토콜은 name
이라는 가져올 수 있는 String
속성에 대한 단일 요구사항을 가집니다. Aged
프로토콜은 age
라는 가져올 수 있는 Int
속성에 대한 단일 요구사항을 가집니다. 두 프로토콜 모두 Person
이라는 구조체에서 채택되었습니다.
또한 이 예제에서는 wishHappyBirthday(to:)
함수를 정의합니다. celebrator
매개변수의 타입은 Named & Aged
로, 이는 "Named
와 Aged
프로토콜을 모두 준수하는 모든 타입"을 의미합니다. 함수에 전달되는 구체적인 타입은 중요하지 않으며, 필요한 두 프로토콜을 모두 준수하기만 하면 됩니다.
그 다음, 예제에서는 birthdayPerson
이라는 새로운 Person
인스턴스를 생성하고 이를 wishHappyBirthday(to:)
함수에 전달합니다. Person
은 두 프로토콜을 모두 준수하므로 이 호출은 유효하며, wishHappyBirthday(to:)
함수는 생일 축하 메시지를 출력할 수 있습니다.
다음은 이전 예제의 Named
프로토콜과 Location
클래스를 결합하는 예제입니다:
class Location { var latitude: Double var longitude: Double init(latitude: Double, longitude: Double) { self.latitude = latitude self.longitude = longitude } } class City: Location, Named { var name: String init(name: String, latitude: Double, longitude: Double) { self.name = name super.init(latitude: latitude, longitude: longitude) } } func beginConcert(in location: Location & Named) { print("Hello, \(location.name)!") } let seattle = City(name: "Seattle", latitude: 47.6, longitude: -122.3) beginConcert(in: seattle) // "Hello, Seattle!"이 출력됩니다.
swift
beginConcert(in:)
함수는 Location & Named
타입의 매개변수를 받습니다. 이는 "Location
의 하위 클래스이면서 Named
프로토콜을 준수하는 모든 타입"을 의미합니다. 이 경우 City
는 두 요구사항을 모두 만족합니다.
birthdayPerson
을 beginConcert(in:)
함수에 전달하는 것은 유효하지 않은데, 그 이유는 Person
이 Location
의 하위 클래스가 아니기 때문입니다. 마찬가지로, Location
의 하위 클래스를 만들었지만 Named
프로토콜을 준수하지 않는다면 해당 타입의 인스턴스로 beginConcert(in:)
을 호출하는 것도 유효하지 않습니다.
프로토콜 조합을 사용하면 여러 프로토콜의 요구사항을 결합하여 더욱 구체적이고 제한적인 타입 요구사항을 표현할 수 있습니다. 이를 통해 코드의 안전성과 명확성을 높일 수 있죠.
다음은 프로토콜 조합의 활용 예시를 보여주는 코드입니다:
protocol Flyable { func fly() } protocol Swimmable { func swim() } struct Duck: Flyable, Swimmable { func fly() { print("오리가 날아갑니다!") } func swim() { print("오리가 수영합니다!") } } func performActions(on creature: Flyable & Swimmable) { creature.fly() creature.swim() } let donald = Duck() performActions(on: donald) // "오리가 날아갑니다!"와 "오리가 수영합니다!"가 출력됩니다.
swift
이 예제에서는 Flyable
과 Swimmable
이라는 두 개의 프로토콜을 정의하고, Duck
구조체에서 이들을 채택합니다. performActions(on:)
함수는 Flyable & Swimmable
타입의 매개변수를 받아 해당 객체의 fly()
와 swim()
메서드를 호출합니다.
donald
라는 Duck
인스턴스를 생성하고 performActions(on:)
함수에 전달하면, 오리가 날아가고 수영하는 동작이 수행됩니다.
이처럼 프로토콜 조합을 활용하면 특정 기능들의 조합을 요구하는 함수나 메서드를 유연하게 정의할 수 있습니다. 이는 코드의 재사용성을 높이고 타입 안정성을 보장하는 데 도움이 됩니다.
프로토콜 조합은 Swift의 강력한 기능 중 하나로, 프로토콜 지향 프로그래밍(Protocol-Oriented Programming)의 핵심 개념입니다. 프로토콜 조합을 적절히 활용하면 코드의 모듈화와 유연성을 크게 향상시킬 수 있습니다.
Swift에서 프로토콜은 매우 중요한 역할을 하며, 프로토콜 조합은 이를 더욱 확장하여 다양한 요구사항을 조합할 수 있게 해줍니다. 프로토콜 조합을 사용하면 클래스 상속 계층과는 별개로 타입에 대한 제약 조건을 명시할 수 있어 코드의 유연성과 재사용성이 높아집니다.
앞으로도 Swift 프로그래밍에서 프로토콜과 프로토콜 조합을 적극 활용하여 더욱 간결하고 표현력 있는 코드를 작성해 보시기 바랍니다!