개요
swift에는 비동기 방식으로 메시지를 전달하는 방식이 몇가지 있습니다. 그중 소개할 방법을 delegate을 소개하려 합니다. delegate라고 한다면 UITableviewDelegate, UITextFieldDelegate등등 다양한 말들을 들어봤을 것이라 생각합니다. 하지만 저도 처음에 delegate에 대한 개념이 조금은 난해했던 것 같습니다. 그래서 예시를 통해서 설명하겠습니다.
본문
delegate패턴이란? https://en.wikipedia.org/wiki/Delegation_pattern
Delegation pattern - Wikipedia
From Wikipedia, the free encyclopedia In software engineering, the delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance. In delegation, an object handles a request by delegatin
en.wikipedia.org
위키를 토대로 말해보자면
"In delegation, an object handles a request by delegating to a second object (the delegate)."
with papago: 위임에서 개체는 두 번째 개체(위임자)에게 위임하여 요청을 처리합니다.
위임 객체는 위임자에게 요청을 처리합니다. 여기서 의문이 듭니다. 위임자는 뭐고 위임객체는 뭘까?? 코드를 통해서 설명드리겠습니다.
우리가 치킨을 판다고 가정해 봅시다. 치킨이 소비자에게 배달되고 먹기까지 많은 일들이 이루어집니다. 간단하게 만들어서 다음과 같은 경로를 거친다고 가정하겠습니다.
치킨을 먹기까지 과정 : "치킨을 요리한다" -> "치킨을 배달한다." -> "치킨을 먹는다"
이렇게 다양한 동작을 코드를 통해 동작하게 되면 적어도 세가지의 책임을 갖는 객체가 필요합니다.
1. "치킨을 요리한다" : 치킨가게 직원, 2. "치킨을 배달한다" : 치킨 배달 드라이버 , 3. "치킨을 먹는다" : 치킨을 먹는 소비자
치킨을 튀기는 직원는 치킨을 배달할 수도 치킨을 먹을수도 없고, 드라이버와 소비자 또한 다른 책임을 갖지 못합니다. 그러니까 치킨가게에서 "치킨을 배달한다.", "치킨을 먹는다" 라는 함수가 들어가는것은 옳지 않습니다. 이를 통해서 치킨튀기는 가게를 코드로 나타내면 다음과 같습니다.
final class ChickenStore{
func cookFrideChicken() {
let chicken = Chicken(type: .firde)
}
}
struct Chicken {
let type:ChickenType
}
enum ChickenType {
case firde
case soySouce
}
우리는 치킨을 드라이버에게 넘기는 함수를 만들어야 합니다. 첫번째로 치킨 가게에서 배달할 수 있다고 가정해 봅시다. (옛날에는 배달대행 안쓰고 직원이 배달했습니다😬)
final class ChickenStore{
func cookFrideChicken() {
let chicken = Chicken(type: .firde)
drive(chickens: chicken, address: "가정집으로")
}
func diliver(chickens: Chicken, address: String) {
}
}
이렇게 배달할 수 있다고 가정해 봅시다. 그런데 만약 배달원이 아프거나 더이상 가게에서 배달을 하지 않는다면 배달대행을 써야 합니다. 배달 대행을 쓸려면 같은 구조체나 클래스가 아닌 다른 곳에서 만들어져야 합니다. 따라서 우리는 다른 class를 통해서 구현해야 합니다.
final class DiliveryCompany {
func diliver(chickens: Chicken, address: String) {
}
}
배달대행은 치킨을 배달합니다. 그러면 치킨가게는 어떻게 배달 대행에게 drive하라고 메시지를 보내야 할까요? 이때 delegate패턴이 사용되는데 치킨가게는 배달을 위임함으로서 배달이란 책임을 갖지 않게 됩니다. 그 과정을 코드로 보여드리겠습니다.
먼저 protocol을 정의 합니다. protoocl은 치킨가게 배달대행이 해야할 책임들을 정의하고 있습니다. 배달대행사는 치킨가게의 배달에 맞게 매서드를 구현합니다.
protocol DiliverChickenDelegate: AnyObject{
func diliver(chickens: Chicken, address: String)
}
final class DiliveryCompany: DiliverChickenDelegate{
func diliver(chickens: Chicken, address: String) {
}
}
그리고 치킨 가게에서 배달대행에 관련 코드를 적습니다.
(1) 치킨 배달을 대신해줄 배달 대행사를 delegate로 상정합니다. (2) 치킨 배달 대행사가 치킨을 전달하라고 메시지를 보냅니다.
final class ChickenStore{
weak var diliveryDelegate: DiliverChickenDelegate? // (1)
func cookFrideChicken() {
let chicken = Chicken(type: .firde)
diliveryDelegate?.diliver(chickens: chicken, address: "가정집으로") // (2)
}
}
여기서 궁금한 점이 생깁니다. 치킨집은 어떻게 배달대행을 알고 있지?? 이부분은 우리가 많이 하던, delegate을 설정해주는 부분과 관련 있습니다. init으로 주는 방법도 있고, 직접 설정해주는 방법도 있습니다. 아래 코드는 bbq치킨집이 빠른 배달 회사를 선택하는 코드를 보여주고 있습니다.
let bbqChicken = ChickenStore()
let fastDiliverCompany = DiliveryCompany() //신속 배달 회사
let accurateDiliverCompany = DiliveryCompany() //정확 배달 회사
bbqChicken.delegate = fastDiliverCompany
이렇게 된다면 bbq치킨집은 다양한 배달 대행 회사중에서 배달을 delegate할 수 있습니다. 외부에서 배달회사를 주입받음으로서 배달을 해줄 수 있습니다. 데이터 흐름은 다음과 같습니다.
"치킨집에서 치킨을 조리한다. (치킨 완성)" -> "치킨" -> "치킨을 배달 대행사가 받고 배달을 시작합니다."
여기서 고객이 배달대행으로 받는 치킨을 받고 먹는 것을 class로 표현하면 다음과 같습니다.
final class Customer {
func receiveChicken(_ chicken: Chicken) {
eatChicken(chicken)
}
func eatChicken(_ chicken: Chicken) {
}
}
그리고 소비자가 치킨을 받는 방식 또한 delegate을 활용하게 된다면, 다음과 같습니다.
final class DiliveryCompany: DiliverChickenDelegate{
weak var customerDelegate: CustomerDelegate?
func diliver(chicken: Chicken, address: String) {
customerDelegate?.receiveChicken(chicken)
}
}
protocol CustomerDelegate: AnyObject {
func receiveChicken(_ chicken: Chicken)
}
final class Customer:CustomerDelegate {
func receiveChicken(_ chicken: Chicken) {
eatChicken(chicken)
}
func eatChicken(_ chicken: Chicken) {
}
}
let maraMincho = Customer()
fastDiliverCompany.customerDelegate = maraMincho
이게 왜 비동기와 연관이 있나요??
비동기와 연관있는 이유는 다음과 같습니다. 치킨을 만들고 배달하는 과정에서 시간이 걸린다고 가정합시다.
고객은 치킨을 받기 위해서 하염없이 문을 바라보며 배달을 기다릴 수도 있습니다. 이를 코드로 나타내면 while문에서 배달이 오는지 안오는지 확인해야 합니다. 그럴 경우 자원이 낭비됩니다. 배달이 왔을 때 초인종을 눌러서 고객에게 알려주기만 하면 자원이 낭비되지 않습니다. 배달 대행이 고객에게 메시지를 주는 방식으로 delegate를 활용하면 고객은 diliver 메시지만 확인하면 됩니다.
레퍼런스 :
https://ssdragon.tistory.com/139
'Swift' 카테고리의 다른 글
[iOS] 모듈화 하기 vs 그냥 살기. (주관적으로 느낀 모듈화 장점 3가지) (0) | 2024.11.22 |
---|---|
[iOS] SwiftData를 활용한 간단한 Todo 어플리케이션 만들어 보기 (0) | 2024.06.02 |
[iOS] Weak Dictionary 다이브 (1) | 2024.02.16 |
[Swift] 여러개의 타입이 존재하는 json을 encode및 decode (0) | 2023.10.01 |
Swift 타입 지정하는 법 (0) | 2023.06.30 |