Language/Swift

Swift 프로토콜 지향 프로그래밍 (POP)

Greensky0026 2021. 3. 17. 16:35
반응형

 애플은 15년에 WWDC에서 Swift는 프로토콜 지향언어라고 말했습니다.

 

 이번 글에선 프로토콜과 익스텐션의 조합으로 프로토콜 지향 프로그래밍을

공부해 서술하도록 하겠습니다.

 

프로토콜 초기구현

 프로토콜 초기구현이란 프로토콜의 요구사항을 익스텐션을 통해 구현하는 것 입니다.

Extension은 기존 타입의 기능을 확장하고, 

Protocol은 프로토콜을 정의할 때 요구사항만을 정의할 뿐 구현은 불가능합니다.

이 두가지를 조합해, 익스텐션에서 프로토콜이 요구하는 기능을 구현해 줄 수 있습니다.

단, 저장 프로퍼티는 익스텐션에서 구현할수 없으므로, 각 타입에서 직접 구현해야합니다.

 

예제

메세지를 수신받는 Receiveable 포로토콜 입니다.

machine과 name를 통해 수신받은 기기와 수신자를 저장합니다.

protocol Receiveable{
    var machine: String {get set}
    var name: String {get set}
    func received(data: Any, from: Sendable)
}

extension Receiveable{
    var machine: String {
        get { return self.machine }
        set { machine = newValue }
    }
    var name: String {
        get	{ return self.name }
        set { name = newValue }
    }
    func received(data: Any, from: Sendable){
        print("\(self.name)의 \(self.machine)으로 \(from.name)가 \(from.machine)으로 보낸 \(data)를 받았습니다.")
    }
}

 

메세지를 전송하는 Sendable 프로토콜입니다.

Receiveable 포로톨과 마찬가지로 machine과 name를 저장하고

send 메서드로 데이터를 전송합니다.

isSendableInstance메소드를 이용해 매개변수로 넣은 인스턴스가

Seneanle을 준수하는지도 확인할 수 있습니다.

extension Sendable{
    var machine: String {
        get { return self.machine }
        set { machine = newValue }
    }
    var name: String {
        get { return self.name }
        set { name = newValue }
    }
    var from: Sendable { return self }
		var to: Receiveable? { get }
    
    func send(data: Any) {
        guard let receiver: Receiveable = self.to else{
            print("수신자가 지정되지 않았습니다.")
            return
        }
        receiver.received(data: data, from: self.from)
    }
    
    static func isSendableInstance(_ instance: Any) -> Bool {
        if let sendableInstance: Sendable = instance as? Sendable {
            return sendableInstance.to != nil
        }
        return false
    }
}

 

Sendable, Receiveable를 상속받는 클래스들을 생성했습니다.

class Message: Sendable, Receiveable {
    var machine = "phone"
    var name: String
    var to: Receiveable?
    init(myName: String){
        self.name = myName
    }
}

class Mail: Sendable, Receiveable {
    var machine = "computer"
    var name: String
    var to: Receiveable?
    init(myName: String){
        self.name = myName
    }
}

 

 

이제, 사용해 볼까요?

let myPhoneMessage: Message = Message(myName: "Greensky")
let yourPhoneMessage: Message = Message(myName: "fireGuy")

myPhoneMessage.send(data: "Hello") //수신자가 지정되지 않았습니다. : to 프로퍼티 미설정
print(Message.isSendableInstance(myPhoneMessage)) //false : to 프로퍼티가 미설정이므로, Message타입 미준수

myPhoneMessage.to = yourPhoneMessage
myPhoneMessage.send(data: "Hello") //fireGuy의 phone으로 Greensky가 phone으로 보낸 Hello를 받았습니다.

let mymail: Mail = Mail(myName: "Greensky")
let youtMail: Mail = Mail(myName: "fireGuy")

mymail.to = yourPhoneMessage
mymail.send(data: "Hello") //fireGuy의 phone으로 Greensky가 computer으로 보낸 Hello를 받았습니다.

종합해보면, 클래스에선 저장 인스턴스 프로퍼티만 값 매핑을 했을 뿐이고

나머지 기능은 프로토콜의 익스텐션에 구현되에 있는걸 볼 수 있습니다.

 

만약 프로토콜의 익스텐션에서 구현한 기능을 변경해서 쓰고 싶다면

override와 같은 별도의 표현 없이 그대로 작성하면 됩니다.

class Mail: Sendable, Receiveable {
    var machine = "computer"
    var name: String
    var to: Receiveable?
    init(myName: String){
        self.name = myName
    }
    
    func send(data: Any){
        print ("Mail의 send 메서드는 재정의되었습니다.")
    }
}

mymail.to = yourPhoneMessage
mymail.send(data: "Hello") //Mail의 send 메서드는 재정의되었습니다.

 

추가로, Receiveable과 Sendable은 machine과 name저장하는 구조가 동일했었죠?

protocol Communicable{
    var machine: String {get set}
    var name: String {get set}
}

extension Communicable {
    var machine: String {
        get    { return self.machine }
        set { machine = newValue }
    }
    var name: String {
        get    { return self.name }
        set { name = newValue }
    }
}

protocol Receiveable: Communicable{
    func received(data: Any, from: Sendable)
}

extension Receiveable{
    func received(data: Any, from: Sendable){
        print("\(self.name)의 \(self.machine)으로 \(from.name)가 \(from.machine)으로 보낸 \(data)를 받았습니다.")
    }
}

protocol Sendable: Communicable{
    var from: Sendable { get }
    var to: Receiveable? { get }
    func send(data: Any)
    static func isSendableInstance(_ instance: Any) -> Bool
}

프로토콜을 추가로 생성해 상속을 사용함으로써 공통된 메서드를 제거해 보았습니다.

 

 


공부에 도움을 준 사이트

 

전반적인 공부 가이드라인

yagom.net/courses/swift-basic/lessons/%ed%94%84%eb%a1%9c%ed%86%a0%ec%bd%9c-%ec%a7%80%ed%96%a5-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d-p-o-p/

 

스위프트 기초 - 야곰닷넷

스위프트는 문법표현의 다양성이 매우 높은 언어입니다. 그래서 스위프트 문법의 모든 형태를 알기는 꽤 오랜 시간이 걸립니다. 그렇지만 최소한의 핵심 문법을 통해 무리없이 스위프트 문법을

yagom.net

공식 스위프트 언어 가이드 번역 홈페이지

jusung.gitbook.io/the-swift-language-guide/language-guide/21-protocols

 

프로토콜 (Protocols)

 

jusung.gitbook.io

 

반응형