Protocol Oriented Language
Swift
- Swift 의 표준 라이브러리에서 타입 정의 부분을 보면 대부분이 구조체로 구현되어 있는 것을 볼 수 있다. (클래스로 구현된 타입도 있기는 하지만 드물다.)
- Swift 가 상속 가능한 클래스 대신 상속 불가능한 구조체로도 다양한 공통 기능 구현이 가능했던 이유 : 프로토콜 & 익스텐션
protocol Talkable {
var topic : String { get set }
func talk(to : Self)
}
struct Person : Talkable {
var topic : String
var name : String
func talk(to : Person) {
print("\(to.name) 에게 \(topic) 에 대해 말하는 \(name)")
}
}
struct Bear : Talkable {
var topic : String
func talk(to : Bear) {
print("\(topic) 에 대해 말하는 곰돌이")
}
}
- [코드 1] : 채택한 프로토콜 안에 정의된 것들은 모두 구현해주어야 하기 때문에 프로토콜을 채택할 때마다 'talk' 메소드를 매번 구현해줘야 하는 번거로움이 있다.
protocol Talkable {
var topic : String { get set }
func talk(to : Self)
}
extension Talkable {
func talk(to : Self) {
print("\(to.name) 에게 \(topic) 에 대해 말하는중 ~")
}
}
struct Person : Talkable {
var topic : String
var name : String
}
struct Bear : Talkable {
var topic : String
}
let r1verfuture = Person(topic : "iOS", name : "r1verfuture")
let r2verfuture = Person(topic : "Swift", name : "r2verfuture")
r1verfuture.talk(to : r2verfuture)
r2verfuture.talk(to : r1verfuture)
- [코드 2] : [코드 1] 과는 다르게 익스텐션으로 'Talkable' 프로토콜을 확장한 다음 확장한 내부에 'talk' 메소드를 구현해주었다.
- 프로토콜을 확장한 내부에 메소드 구현부를 넣어주면 해당 프로토콜을 채택한 타입 내부에 해당 메소드를 구현해주지 않아도 문제없다.
- 만약에 프로토콜 초기 구현과 다르게 동작해야 하는 경우에는 재정의해주기만 하면 된다. ([코드 3] 참고)
protocol Talkable {
var topic : String { get set }
func talk(to : Self)
}
extension Talkable {
func talk(to : Self) {
print("\(to.name) 에게 \(topic) 에 대해 말하는중 ~")
}
}
struct Bear : Talkable {
var topic : String
var name : String
func talk(to : Bear) {
print("\(to.name) 에게 \(topic) 에 대해 말하는중 ~")
}
}
let whatso = Bear(topic : "Disney", name : "왓소")
let lotso = Bear(topic : "Toy Story", name : "랏소")
whatso.talk(to : lotso)
lotso.talk(to : whatso)
- [코드 3] : 'Talkable' 프로토콜에 있던 기존 'talk' 메소드를 재정의해서 'Bear' 구조체에 구현해주었다.
Protocol Oriented Programming (POP) 을 쓰는 이유
- 클래스는 참조 타입이기 때문에 참조 추적에 비용이 많이 발생한다.
- 참조 타입과 비교했을 때 값 타입은 비용이 적게 들지만, 상속이 불가능해서 기능을 다시 구현해줘야 하는 한계를 없애버렸다.
참고