델리게이션 및 프로토콜 컴포지션에 대해 알아 보겠습니다.
델리게이션
델리게이션은 어느 한 타입의 인스턴스가 다른 인스턴스를 대신해서 동작하는 상황에 잘 맞는다.
동작을 위임하는 인스턴스는 델리게이트 인스턴스의 참조를 저장하고 있다가 어떠한 동작이 발생하면 델리게이팅 인스턴스는 계획된 함수를 수행하기 위해 델리게이트를 호출 합니다.
예제
<코딩관점>
프로토콜 구현
protocol DisplaynameDelegate {
func displayName(name: String)
}
델리게이트를 사용하는 Person구조체
struct Person {
var displaynameDelegate: DisplayNameDelegate
var firstName = "" {
didSet {
displaynameDelegate.displayName(name: firstName)
}
}
var lastName = "" {
didSet {
displaynameDelegate.displayName(name: lastName)
}
}
init(displayNameDelegate: DisplayNameDelegate) {
self.displaynameDelegate = displayNameDelegate
}
func getFullName() -> String {
return "\(firstName) \(lastName)"
}
}
DisplayNameDelegate 프로토콜을 따를 타입 생성
struct MyDisplayNameDelegate: DisplayNameDelegate {
func displayName(name: String) {
print("Name: \(name)")
}
}
자 이제 한번 이름을 출력해볼까요?
델리게이트를 어떻게 사용하지는 봅시다.
protocol DisplayNameDelegate {
func displayName(name: String)
}
struct Person {
var displaynameDelegate: DisplayNameDelegate
var firstName = "" {
didSet {
displaynameDelegate.displayName(name: firstName)
}
}
var lastName = "" {
didSet {
displaynameDelegate.displayName(name: lastName)
}
}
init(displayNameDelegate: DisplayNameDelegate) {
self.displaynameDelegate = displayNameDelegate
}
func getFullName() -> String {
return "\(firstName) \(lastName)"
}
}
struct MyDisplayNameDelegate: DisplayNameDelegate {
func displayName(name: String) {
print("Name: \(name)")
}
}
var displayDelegate = MyDisplayNameDelegate()
var person = Person(displayNameDelegate: displayDelegate)
person.firstName = "승진"
person.lastName = "김"
//출력
Name: 승진
Name: 김
<설계관점>
프로토콜지향 프로그래밍에서는 프로그램을 설계할 때 항상 프로토콜에서 시작하긴 하지만, 프로토콜은 어떠게 설계할 수 있을까요?
객체지향은 서브클래스를 위한 모든 기본적인 요구 사항을 포함하는 슈퍼클래스를 갖는 반면, 프로토콜 설계방식은 슈퍼클래스 대신 프로토콜을 사용하며, 이는 요구 사항을 더 큰 덩어리의 프로토콜이 아닌 작고 매우 구체적인 프로토콜로 나누기에 적절합니다.
로롯을 모델링하여 프로토콜 기반으로 간단한 설계를 해보죠
먼저 움직임에 대한 요구사항을 정의 하겠습니다.
protocol RobotMovement {
func forward(speedPercent: Double)
func reverse(speedPercent: Double)
func left(speedPercent: Double)
func right(speedPerent: Double)
func stop()
}
3차원상에서 움직이게 하기 위한 프로토콜
protocol RobotMovementThreeDimensions: RobotMovement {
func up(speedPercent: Double)
func down(speedPercnet: Double)
}
센서 프로토콜
protocol Senser {
var sensorType: String { get }
var sensorName: String { get set }
init(sensorName: String)
func pollSensor()
}
센서 타입에 대한 프로토콜을 채택한 환경 센서에 대한 프로토콜'
protocol EnvironmentSensor: Sensor {
func currentTemperature() -> Double
func currentHumidity() -> Double
}
좀 더 많은 센서 타입을 만들어 봅시다.
protocol RangeSensor: Senser {
func setRangeNotification(rangeCentimeter: Double, rangeNotification: () -> ())
func currentRange() -> Double
}
protocol DisplaySensor: Senser {
func displayMessage(message: String)
}
protocol wirelessSensor: Senser {
func setMessageReceivedNotification(messageNotification: (String) -> Void)
func messageSend(message: String)
}
이와같이 프로토콜 지향 설계에서 얻을 수 있는 장점에는 두 가지가 있다.
- 각 프로토콜은 특정 센서 타입에서 필요한 구체적인 요구 사항만들 포함한다.
- 프로토콜 컴포지션을 사용해 단일 타입이 다중 프로토콜을 따르게 할 수 있다.
protocol Robot {
var name: String { get set }
var robotMevement: RobotMovement { get set }
var sensors: [Senser] { get }
init(name: String, robotMovement: RobotMovement)
func addSensor(sensor: Senser)
func pollSensors()
}
Robot프로토콜을 따르는 세 개의 프로퍼티와 2개의 메소드를 정의 하고 있습니다.
슈퍼클래스 타입을 사용하는 것에만 익숙해 있다면 이 모든 프로토콜에 대해 생각한다는것이 혼란스러울지도 모릅니다.
좀 더 쉽게 이해하기 위해 다이어그램으로 나타내보겠습니다.
프로토콜을 사용해서 로봇의 컴포넌트에 대한 요구사항을 정의 해보았습니다.
이런식으로 프로토콜을 사용하여 매우 구체적인 요구 사항을 만드는 데 프로토콜을 사용할 수 있고, 그런 다음 프로토콜 계층 구조를 만들기 위해 프로토콜 상속과 프로토콜 컴포지션을 어떻게 사용하는지에 대해 알아 보았습니다.
참고
스위프트4 프로토콜지향 프로그래밍 3/e
에이콘
'Swift' 카테고리의 다른 글
[Swift] 고차함수(2) - map, flatMap, compactMap (0) | 2019.07.25 |
---|---|
[Swift] 고차함수(1) - forEach, filter, reduce (0) | 2019.07.25 |
[Swift] 프로토콜 지향 프로그램 - 1 (프로토콜이란) (0) | 2019.07.24 |
[Swift] 프로토콜 지향 프로그래밍(Protocol-Oriented Programming) (0) | 2019.07.24 |
[Swift] Magnitude, Abs (0) | 2019.07.24 |