애플은 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
}
프로토콜을 추가로 생성해 상속을 사용함으로써 공통된 메서드를 제거해 보았습니다.
공부에 도움을 준 사이트
전반적인 공부 가이드라인
공식 스위프트 언어 가이드 번역 홈페이지
jusung.gitbook.io/the-swift-language-guide/language-guide/21-protocols
'Language > Swift' 카테고리의 다른 글
Swift 서브스크립트 (Subscripts) (0) | 2021.03.21 |
---|---|
Swift 제네릭 (Generic) (0) | 2021.03.19 |
Swift 접근제어 (0) | 2021.03.16 |
Swift 고차함수 (0) | 2021.03.15 |
Swift 오류처리 (0) | 2021.03.15 |