프로토콜(Protocol)

  • 인터페이스(interface)와 구현(implementation)
  • 클래스: interface + implementation
  • 프로토콜: only interface
    • implementation이 없어서 단독 사용 불가
    • 클래스, 구조체와 함께 사용
    • 메소드, 프로퍼티 구현을 약속 함

프로토콜 정의

protocol PROTOCOL_NAME { }

프로토콜 채택

class CLASS_NAME: PROTOCOL_NAME {}

프로토콜 사용 예제

* 프로토콜 정의
protocol Singing { 
	func sing()
}

* 클래스 채택 
class Bird: singing {
	func sing() {
		print("짹짹짹")
	}
}

* 객체 생성과 사용 
var sparrow = Bird()
sparrow.sing() // 짹짹쨱 

- 같은 인터페이스, 다양한 구현

  • 클래스, 구조체, enum 채택
  • 프로토콜에 선언한 메소드를 미구현시 -> 컴파일 에러
class Bird: singing {
	func sing() {
		print("짹짹짹")
	}
}

class Cat: singing {
	func sing() {
		print("갸르릉 갸르릉")
	}
}
  • 다중 프로토콜 채택
protocol Singing { 
	func sing()
}

protocol Dancing {
	func dance() 
}

class Human: Dancing, Singing {
	func sing() {
		print("랄라라~")
	}
	func dance() {
		print("춤추자")
	}
}

- 프로토콜 상속과 구현

protocol Singing {
	func sing() 
}

protocol Dancing {
	func dance() 
}

protocol Entertaining: Singing, Dancing {
}

struct Human: Entertaining {
	func sing() {}
	func dance() {}
}

- 프로토콜 내 프로퍼티 선언

  • get/set 행위만 선언
  • get 전용, get/set 모두
  • set 전용은 없음
  • 타입 프로퍼티 선언 가능
* 프로토콜  프로퍼티 선언 
protocol HoldingBreath {
	var duration: Int {get set}
}

* 계산 프로퍼티 구현

class MyClass: HoldingBreath {
	var duration: Int {
		get {
			return 0 
		}set {}
	}
}

* 저장 프로퍼티 구현 

struct MyStruct: HoldingBreath {
	var duration: Int 
}

- 프로토콜 내 Initializer

protocol Named {
	init(name: String) 
}

class Monster: Named {
	var name: String?
	required init(name: String) {
		self.name = name 
	}
}

> convenience initializer에서 required 추가 해야함.
> struct에는 required 사용하지 않음 

struct Boss: Named {
	init(name: String) {}
} 


프로토콜 타입

  • 프로토콜을 타입으로 사용 & 선언
  • 프로토콜 내 메소드, 프로퍼티만 사용 가능하다
protocol Singing {
	func sing() 
}

protocol Dancing {
	func dance() 
}

protocol Entertaining: Singing, Dancing {
}

struct Human: Singing {
	func sing() {}
	func dance() {}
}

var singinganimal: Singing = Human()
singinganimal.sing() // 가능 
singinganimal.dance() // 불가능 

class Bird : Singing {
    func sing() {
        print("짹짹짹~")
    }
}

let Animal: Dancing = Bird() // 타입 오류 

* 다수의 프로토콜 타입 
func entertain(who: Singing & Dancing) {}
	-> singing, Dancing 프로포콜을 체택한 객체를 파라미터로 사용함.

클래스 전용 프로토콜

클래스 에서만 채택 가능

protocol MyProtocol: class {}
	-> 구조체, enum 에서 채택시 컴파일 에러 발생. 

프로토콜 타입을 weak와 사용할 
class Myclass: MyProtocol {
	weak var property: MyProtocol!
}
	-> 프로토콜로 타입을 선언하는것은 class, struct, enum 모두 가능하지만, ARC는 클래스만 가능함. 따라서, 프로토콜 타입으로 선언한 변수를 weak 사용하기 위해서는 class 에서만 사용 가능함. 

Extension

  • 타입을 나눠서 작성 가능합니다
  • 하나의 타입으로 동작합니다
  • 기존에 작성된 타입을 확장
  • 서로 다른 파일에 작성 가능

- Extension으로 가능한 것

  • 계산 프로퍼티
  • 메소드
  • 프로토콜
  • 서브스크립트
  • nested type

- Extension으로 불가능한 것

  • designated initializer
    • Convenience initializer는 추가 가능합니다.
  • 저장 프로퍼티
class Dog {
	func eat() {
		print("밥 먹기") 
	}
}

extension Dog {
	// 저장 프로퍼티는 사용 불가
	var color: String! // 컴파일 에러 
	
	// 계산 프로퍼티 사용가능
		var everageLife: Int! {return 15}
}

POP(Protocol Oriented Programming)

  • Protocol Extension에 구현 코드 작성 가능함
  • Protocol을 채택해서 Extension으로 구현 코드 작성 가능
  • 다중 상속과 비슷한 효과
  • 구조체/Enum 사용 시 중복 코드 문제 해결
  • 구조체와 Protocol, extension을 활용한 프로그래밍 기법
* Protocol과 Protocol extension
 
protocol Movable { } 
extension Movable {
	func move() { 
		print("Go! go! go!")
	} 
}

* Protocol 채택, 객체를 이용한 호출 
struct Human : Movable { } 
var man = Human() 
man.move()

다중 Protocol 채택

  • 다중 상속 효과
  • 기본 구현 재정의
class Superman : Movable, Flyable { 
	func move() {
		print("Move Fast") 
	}
}
var superman = Superman() 
superman.move() 
superman.fly()

마치 2개의 클래스에서 상속을 받은것 같은 효과를 냅니다, 그리고 함수 move()는 기존에 정의했던것을 재정의 할수 있음.(하지만 단점으로는 언제 재정의 되었는지 모호해짐..)


POP(Protocol Oriented Programming) 장점

  • 코드 중복이 발생하는 상황 -> 상속이 없는 구조체로 만들수 있음
  • Struct, Enum을 사용할때 사용하면 좋음
  • 성능적 이득을 볼수 있음

* 코드 중복이 발생 하는 상황 
struct Bird { 
	func move() {
		print("이동하다") 
	}
	func fly() { 
		print("날다")
	} 
}

struct Airplane { 
	func move() {
		print("이동하다") 
	}
	func fly() { 
		print("날다")
	}
}

* Protocol Extension으로 코드 중복 제거 

protocol Movable {} 
extension Movable {
	func move() { 
		print("이동하다")
	} 
}

protocol Flyable {} 
extension Flyable {
	func fly() { 
		print("날다")
	}
}

struct Bird : Movable, Flyable { }
struct Airplane: Movable, Flyable { }

코드 중복 제거, Protocol, Extension을 가지고 중복 코드를 제거할수 있음!


Reference

Swift에서 프로토콜 중심 프로그래밍(POP)하기
Protocol-Oriented Programming in Swift
Protocol and Value Oriented Programming in UIKit Apps