Delegate?
진짜 Delegate 이거.. 너무 복잡하게 생각했던건가 생각도 드는데..
이해가 조금 되서 쓰는 것도 있지만, 이해하고 정리하려 포스팅하는 것도 없지 않아 있다.
그냥 우선 쉽게 접근하기 위해 Delegate 뜻이 뭔지부터 알아보자
뭐 더 많은 뜻이 있겠지만, 우리가 알아야하는 Delegate의 뜻은 "위임하다" 이다. (영국식으로 ㅋㅋ)
우리는 위임을 알아야한다.
위임은 내가 할 일은 다른 사람에게 넘기는 것, 대신 시키는 것 이라는 의미를 가진다.
위임하면 사장-직원 같은 수직적인 관계가 생각나는데, 위임은 그저 내가 할 일을 누군가에게 시키는 것이기 때문에 수평적인 관계에서도 가능하다.
들어가기 전 먼저 알아야 할 핵심은 수직/수평적 관계를 따지는 것이 아닌 내가 할 일을 을 다른 누군가에게 시키는 것 임을 알자.
근데 누구한테 위임을 하고 자시고 이걸 왜 쓰는건데?가 궁금할 것이다.
왜 쓰는것인가?
객체지향 세계에선 역할과 책임이라는 중요한 문구가 있다.
예를 들어 SOLID에서 단일책임원칙이라던지, 한 함수의 기능, 책임은 하나여야 된다 던지..
Delegate는 책임을 넘겨주기 위해 사용된다.
실생활에서 위임은 어떻게 쓰이고 있을까?
- 내가 하는 일이 너무 많아서, 즉 지는 책임이 너무 많아서 이를 다른 사람에게 넘겨주는 경우
- 피치못할 사정이 생기거나, 일 하기 싫을 때 남한테 넘겨주는 경우
대통령 권한 대행, 배송 대행지 이런 것도 위임으로 볼 수 있다.
"아 이건 못할 것 같아 너가 해줘", "아 바빠/귀찮아 너가 해줘" 이렇게 자신의 책임을 남에게 넘기는 것 같이 실생활에서 빈번하게 위임은 일어나고 있다.
프로그래밍 세계에선 자신의 책임을 넘김으로써 위임한 자, 위임받은 자의 책임과 역할을 분명하게 할 수 있고, 그로 인해 가독성, 로직의 흐름을 따라가기 쉬워지는 결과를 얻을 수 있다.
음.. 객체를 단순화시키고 추상에 의존함으로써 의존성도 떨어뜨릴 수 있는.. 이건 밑에서 설명하겠다.
Delegate
위에선 수평/수직적 관계 상관없다고 그랬는데 떠오르는 예가 이것뿐이 없어서 사장과 직원과의 관계로 예를 들어 설명해보겠다.
(주)지성이라는 회사의 사장인 지성이 직원에게 "리스트 던져줄테니깐 개발해봐" 라고 일을 시켰다고 생각해보자.
아 물론 회사의 사장인 지성도 개발할 수 있다는 가정이 있다.
먼저 사장 객체를 만들어보자.
class CEO {
var developmentList: [String: Bool]
var employee: Employee
init(developmentList: [String: Bool], employee: Employee) {
self.developmentList = developmentList
self.employee = employee
self.employee.delegator = self
}
func notifyDevelopeList() -> [String] {
return developmentList.keys.map { $0 }
}
}
이니셜라이저를 보면 직원(Employee)의 위임자는 self (CEO 자기 자신)으로 초기화되고 있다. -> CEO가 직원에게 위임했다.
CEO는 개발 리스트를 작성하고 작성된 리스트를 직원에게 던져주는 역할을 하고 있다.
그럼 직원은 그 개발 리스트를 받아서 개발을 하고 CEO에게 개발 다 됐어요 하고 알려야겠지?
class Employee {
var delegator: CEO?
func develope() {
let developmentStatus = delegator?.notifyDevelopeList()
delegator?.developmentList.updateValue(true, forKey: (developmentStatus?.first)!)
}
}
강제 언래핑은.... 이해해주세요.. 직원이 시킨대로 개발 착착 다 하면 때려치고 회사 차릴까봐.. 하나만 완료하게 하려다 그만..
let employee1 = Employee()
let ceo = CEO(developmentList: ["지성플레이어":false, "지플릭스":false, "지성e북":false], employee: employee1)
employee1.develope()
ceo.developmentList // ["지플릭스": true, "지성플레이어": false, "지성e북": false]
CEO에게 일을 위임받은 employee1이 개발을 하니, ceo의 개발리스트가 업데이트 된 모습을 볼 수 있다.
어 근데 이거.. 이러면 Delegate를 왜 쓰는거여? 그냥 직접 접근해서 값 바꿔주는 거잖아? 생각이 들어야 한다.
맞다. 위 코드는 위임을 한 것은 맞지만 서로 의존성이 강하다.
사장이 위임할 사람으로 지정한 직원이 없으면 회사 쫄딱 망하는거고, 직원 또한 저 사장이 아니면 굶어 죽는 것이다.
그래서 Swift에서의 Delegate는 필수적으로 Protocol을 사용한다.
의존성을 줄이기 위해 서로에게 의존하는 것이 아닌 Protocol이라는 추상에 의존을 하게끔하여 서로를 모르는 사이로 만들어 버리는 것이다.
사장은 개발할 수 있는 직원이 필요한거지 특정 직원이 필요한 것이 아니다.
더불어 직원은 일 할 수 있는 회사(사장)이 필요한거지 꼭 저 회사(사장)에서 일 할 필요가 없다.
그럼 우린 "개발할 수 있는" 과 "개발 시킬 수 있는" 으로 추상화 해볼 수 있다.
protocol Developable {
func develope()
}
protocol DevelopementOrderable {
var developmentList: [String: Bool] { get set }
func notifyDevelopeList() -> [String]
}
이제 사장이 달라지든, 직원이 달라지든 해당 프로토콜만 채택해준다면 누구든 일을 할 수 있고, 일을 시킬 수 있는 책임과 역할을 얻을 수 있다.
class CEO: DevelopementOrderable {
var developmentList: [String: Bool]
var employee: Developable?
init(developmentList: [String: Bool]) {
self.developmentList = developmentList
}
func notifyDevelopeList() -> [String] {
return developmentList.keys.map { $0 }
}
}
class TeamLeader: DevelopementOrderable {
var developmentList: [String: Bool]
var subordinate: Developable?
init(developmentList: [String: Bool]) {
self.developmentList = developmentList
}
func notifyDevelopeList() -> [String] {
return developmentList.keys.map { $0 }
}
}
DevelopementOrderable을 채택하니 꼭 사장이 아닌 팀장도 부하직원에게 일을 시킬 수 있게 됐다.
class Employee: Developable {
var delegator: DevelopementOrderable?
func develope() {
let developmentStatus = delegator?.notifyDevelopeList()
delegator?.developmentList.updateValue(true, forKey: (developmentStatus?.first)!)
}
}
class CodingGod: Developable {
var delegator: DevelopementOrderable?
func develope() {
let developmentStatus = delegator?.notifyDevelopeList()
developmentStatus?.forEach {
delegator?.developmentList.updateValue(true, forKey: $0)
}
}
}
이제 특정 사람(클래스)가 아닌 Developable을 채택한 사람이라면 직원, 부하직원이 될 수 있다.
이전에 의존성이 강했던 코드와 달리 사장과 직원은 서로 의존하지 않고, 추상에 의존하므로 서로를 모르는(의존하지 않는)사이가 되었다.
해당 프로토콜만 채택하고 있다면 위임에도 제한이 없을 것이다.
let employee1 = Employee()
let genius = CodingGod()
let ceo = CEO(developmentList: ["지성플레이어":false, "지플릭스":false, "지성e북":false])
let leader = TeamLeader(developmentList: ["성지플레이어":false, "성플릭스":false, "성지e북":false])
employee1.delegator = ceo
employee1.develope()
print(ceo.developmentList) // ["지성플레이어": true, "지플릭스": false, "지성e북": false]
employee1.delegator = leader
employee1.develope()
print(leader.developmentList) // ["성지e북": true, "성플릭스": false, "성지플레이어": false]
genius.delegator = leader
genius.develope()
print(leader.developmentList) // ["성지e북": true, "성지플레이어": true, "성플릭스": true]
위의 프로토콜을 채택만 해준다면 어떤 클래스든 위임받고 할 수 있다.
이미 정의된 Delegate 프로토콜도 많은데, 그것들도 다 똑같다.
진짜 간단하게 생각해보면 된다.
밑에 예에서 "나"를 TextField로 생각해보자
TextField: "아 내가 할일이 많아.. 그래서 이 일 좀 누구한테 넘겨야겠어"
TextField: "야, X야 너가 이것 나 대신 좀 해라"
X: 아.. 네..
그럼 X측 관점으로 보면 쟤(TextField)의 일을 내가 대신 하는 것이다.
간단한 코드로 보면 (그냥 대충 짜본 것이다. 컴파일 오류날 것임)
class X {
TextField.delegate = self // TextField가 일 위임하는 애는 나임, 쟤가 나한테 일 시킴
}
이렇게 되는 것이다.
근데 TextField의 관점으로 보면?
쟤(X)가 나(TextField)의 일을 대신 하는 거니
class TextField {
var delegate = X
}
이렇게 된다.
근데 딱봐도 밑에건 드럽게 헷갈리지 않나?
그래서 개인적으로 생각한 것은 위임받는 자의 관점으로 생각하면 훨씬 간단해질 것 같다.
아ㅏㅏㅏ 내일 테이블뷰로 더 알아보겠다.
설명 진짜 못한 것 같아서 우울하다.
'iOS > Swift' 카테고리의 다른 글
Swift) Codable, Decode, Encode (0) | 2022.02.13 |
---|---|
Swift) TableView (0) | 2022.02.13 |
SOLID (0) | 2022.02.13 |
Swift) App LifeCycle (0) | 2022.02.13 |
Swift) weak, unowned (0) | 2022.02.13 |
댓글