cleanUrl: /programming/generic-protocol

저는 처음에 Generic Protocol이란 말을 들었을 때 굉장히 헷갈렸습니다. Protocol만 해도 충분히 추상적인데, 왜 Generic이라는 다른 추상적 개념이 끌어들여 더더욱 추상적인 Generic Protocol이란 개념을 만들어야만 했는가.. 라는 생각까지도 했었죠.

하지만 이름만 들었을 때는 이렇게 막연하고 헷갈리는 개념도, 차근차근 짚어 보면 그렇게 어렵지 않습니다.

해결하려는 문제

많은 타입들이 어떤 형태로든 “식별자(ID)”를 가지고 있습니다. 아래 코드 처럼요.

class Student {
    var ID: Int
}

이렇게 식별자를 가지고 있는 모든 클래스들을, Identifiable 이라는 프로토콜로 추상화 할 수 있을 것 같습니다.

protocol Identifiable {
    var ID: Int { get set }
}

class Student: Identifiable {
    var ID: Int
}

그런데 어떤 종류 클래스들은 문자열 형태의 식별자를 채택하는 경우도 있습니다.

class AppleDevice {
    var ID: String = "a123-324adf-sdf" // 시리얼넘버들 같은 경우, 식별자에 문자열이 포함되기도 하죠
}

근데, 그럼 이제 AppleDevice는 Identifiable 을 충족시킬 수 없게 됩니다. Identifiable의 ID는 Int타입으로 정해져 있기 때문이죠.

그럼 이제 어떻게 해야 할까요? 지저분한 방법이지만, 아래와 같이 문제를 해결 할 수 있을 것 같습니다.

protocol IntIdentifiable {
    var ID: Int { get set }
}

protocol StringIdentifiable {
    var ID: String { get set }
}

class Student: IntIdentifiable {
    var ID: Int // 학번
}

class AppleDevice: StringIdentifiable {
    var ID: String // 시리얼번호
}

하지만 정말 지저분하네요. IntIdentifiable 이나 StringIdentifiable 같은 프로토콜을 만드는 방법 말고, 좀 더 깔끔한 방법은 없을까요? 있습니다! 그것은 다름 아닌… Generic Protocol 입니다!!(두둥!!)

How does it Solve?

‘ID’의 타입을 프로토콜에서 바로 지정해버리기 때문에 위와 같은 문제가 일어났습니다. 따라서 이 문제를 해결하려면, 프로토콜에서는 ‘ID’의 타입을 모르도록 해야합니다. 여기서 associatedtype 이라는 키워드가 들어옵니다.

protocol Identifiable {
    associatedtype T
    var ID: T { get set }
}

associatedtye 은, Generic Type이나 Generic Function 을 만들 때 쓰던 “” 의 “<>”랑 비슷한 역할을 한다고 보면 됩니다. T 는 당연히 “”의 T가 되겠고요. 즉, T는 타입값인데, 프로토콜이 지정해주진 않고, 프로토콜을 채택하는 클래스 등에서 지정해주라는 표시입니다. 따라서 우리는 아래와 같이 쓸 수 있습니다.

protocol Identifiable {
    associatedtype ID_TYPE
    var ID: ID_TYPE { get set }
}

class Student: Identifiable {
    var ID: Int
    init(학번: Int) {
        self.ID = 학번
    }
}

let John = Student(학번: 301)

class AppleDevice: Identifiable {
    var ID: String
    init(serialNumber: String) {
        self.ID = serialNumber
    }
}

let iPhone = AppleDevice(serialNumber: "123-9xs90-dfx182")

즉, Generic Protocol은, Protocol에서 정의하는 프로퍼티나 메소드의 파라미터들의 타입을 Generic으로 받는 프로토콜이라고 설명 할 수 있을 것 같습니다.