프로토콜 확장은 스위프트에서 프로토콜 지향 프로그래밍의 이념을 지원하기 위해 도입되었습니다: 프로그래머는 기본 클래스 조상이나 전역 함수를 통해서가 아니라 프로토콜 수준에서 직접적으로 기본 메소드 구현을 추가할 수 있습니다. 이를 통해 코드 중복을 줄이고 타입의 동작을 유연하게 조정할 수 있습니다.
문제는 기본 구현이 고유한(override) 구현의 필요성을 가릴 때나 여러 프로토콜 간의 책임이 불명확해질 수 있을 때 발생합니다 — 특히 타입이 교차하는 여러 프로토콜을 구현하는 경우에. 또한, 타입 내에서 메소드가 구현되면 항상 확장 내의 구현을 가릴 수 있다는 점을 기억하는 것이 중요합니다.
해결책은 프로토콜 확장을 보편적인 동작에만 사용하는 것이고, 특정 경우에는 타입 내에서 명시적으로 메소드를 구현하는 것입니다. 동일한 프로토콜의 두 개의 확장에서 같은 메소드의 오버로드를 피하는 것이 바람직합니다.
코드 예시:
protocol Flyer { func fly() } extension Flyer { func fly() { print("기본 비행") } } struct Bird: Flyer {} let sparrow = Bird() sparrow.fly() // 출력: 기본 비행
주요 특징:
프로토콜 확장에서 프로토콜에 저장 프로퍼티를 추가할 수 있나요?
아니요, 프로토콜 확장에는 계산 프로퍼티와 메소드만 추가할 수 있으며 저장 프로퍼티는 금지되어 있습니다.
프로토콜과 타입에 동일한 이름의 메소드가 다른 구현이 있을 경우 어떻게 되나요? 어떤 것이 호출되나요?
타입에 직접 접근할 경우 타입의 구현이 호출되며, 프로토콜 타입의 참조를 통해 인스턴스에 접근할 경우 프로토콜 확장의 구현이 호출됩니다.
protocol Greeter { func greet() } extension Greeter { func greet() { print("확장에서 안녕하세요") } } struct Person: Greeter { func greet() { print("타입에서 안녕하세요") } } let person = Person() person.greet() // 타입에서 안녕하세요 let greeter: Greeter = person greeter.greet() // 확장에서 안녕하세요
프로토콜 확장에서 제약 조건에 where 제약 조건을 사용할 수 있나요?
네. 이것은 강력한 기능 중 하나로, 특정 타입에 대해서만 프로토콜을 확장할 수 있습니다.
extension Collection where Element: Equatable { func allEqual() -> Bool { guard let first = self.first else { return true } return allSatisfy { $0 == first } } }
부정적 케이스
팀에서 프로토콜 확장을 통해 오류 로깅을 구현하기로 결정했으나, 각 서비스가 특정 형식을 추가하고 싶어할 수 있다는 점을 고려하지 않았습니다. 결국, 서로 다른 서비스가 프로토콜 참조를 통해 함수를 호출하면서 동작의 로직이 기대와 달라졌습니다.
장점:
단점:
긍정적 케이스
프로토콜은 항상 보편적인 동작에 대해서만 확장됩니다. 특별한 경우에는 타입 내에서 메소드를 명시적으로 구현하며, 충돌하는 부분에 대해 코드 리뷰가 있습니다.
장점:
단점: