Swift 프로토콜 지향 프로그래밍 (POP)
애플은 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
공식 스위프트 언어 가이드 번역 홈페이지
jusung.gitbook.io/the-swift-language-guide/language-guide/21-protocols
프로토콜 (Protocols)
jusung.gitbook.io