델리게이션 및 프로토콜 컴포지션에 대해 알아 보겠습니다.
델리게이션
델리게이션은 어느 한 타입의 인스턴스가 다른 인스턴스를 대신해서 동작하는 상황에 잘 맞는다.
동작을 위임하는 인스턴스는 델리게이트 인스턴스의 참조를 저장하고 있다가 어떠한 동작이 발생하면 델리게이팅 인스턴스는 계획된 함수를 수행하기 위해 델리게이트를 호출 합니다.
예제
<코딩관점>
프로토콜 구현
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 |