์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- raywenderlich
- visionOS
- swift
- ios
- UIKit
- arkit
- SnapKit
- Lv2
- ๋ฐฑ์ค
- Xcode
- BFS
- Kuring
- XCTest
- combine
- rxcocoa
- node.js
- MVVM
- reactorkit
- BOJ
- Swfit
- TCA
- SwiftUI
- designpattern
- tableView
- RxSwift
- ํ๋ก๊ทธ๋๋จธ์ค
- ํจ์คํธ์บ ํผ์ค
- Flutter
- CollectionView
- realm
- Today
- Total
lgvv98
[Swift] Multicast Delegate Pattern ๋ณธ๋ฌธ
[Swift] Multicast Delegate Pattern
๐ฅ ์บ๋ฟ๋งจ 2022. 6. 7. 10:45Multicast Delegate Pattern
โ Multicast Delegate Pattern
์๋์ ๋ฌธ์๋ฅผ ๊ตฌ์ ํ์ฌ ์์ด ๋ฌธ์๋ฅผ ๋ฒ์ญํ๊ณ ์ดํดํ ๊ฒ์ ๋ฐํ์ผ๋ก ๊ธ์ ์์ฑํ๊ณ ์์ต๋๋ค.
multicast delegate ํจํด์ delegate ํจํด์ ๋ณํ์ธ ํ๋ ํจํด์ด๋ค. ์ด๋ฅผ ํตํด ๋จ์ delegate์ 1:1 ๊ด๊ณ ๋์ 1:N delegate ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
1. object needing a delegate(a.k.a delegating object)๋ ํ๋ ํน์ ๊ทธ ์ด์์ delegate๋ฅผ ๊ฐ์ง๋ ๊ฐ์ฒด์ ๋๋ค.
2. delegate protocol์ ๊ตฌํํ๊ฑฐ๋ ๊ตฌํํด์ผํ๋ ๋ฉ์๋๋ฅผ ์ ์ํฉ๋๋ค. (protocol ์ ์์์ optional๋ก ๋ง๋๋ ๊ธฐ๋ฒ์ด ์์ต๋๋ค.)
3. delegate(s)๋ delegate protocol์ ๊ตฌํํ๋ ๊ฐ์ฒด์ ๋๋ค.
4. multicast delegate๋ delegate๋ฅผ ๋ณด์ ํ๋ helper class์ ๋๋ค. delegate-worthy ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๋๋ง๋ค ๊ฐ์์๊ฒ ํต์งํฉ๋๋ค.
๋ฉํฐ์บ์คํธ ๋๋ฆฌ๊ฒ์ดํธ ํจํด๊ณผ ๋๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฃผ์ ์ฐจ์ด์ ์ ๋ฉํฐ์บ์คํธ ๋๋ฆฌ๊ฒ์ดํธ๋ฅผ ์ํด์ helper class๊ฐ ์๋ค๋ ์ ์ ๋๋ค. Swift๋ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด ํด๋์ค๋ฅผ ์ ๊ณตํ์ง ์์ผ๋, ์ฐ๋ฆฌ๊ฐ ์ด๋ฒ ์๊ฐ์ ๋ง๋ค์ด ๋ณผ ์์ ์ ๋๋ค.
Note: Swift 5.1์์ Combine ํ๋ ์์ํฌ ์์์ Multicast ์ ํ์ ๋์ ํ์ต๋๋ค. Combine ํ๋ ์์ํฌ ๋ด์ ์๋ ๊ฒ์ ์ฌ๊ธฐ์ ์ด์ผ๊ธฐํ๋ MulticastDelegate์๋ ๋ค๋ฆ ๋๋ค.
Combine์ ๋ํด์ ๋ ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ์๋ ๋งํฌ๋ฅผ ํ์ธํด์ฃผ์ธ์!
(RxSwift ๋งํผ Combine์ด ์ ๋ฆฌ๊ฐ ์ ๋์ด ์์ด์, ๋์ค์ ์ฌ์ ๊ณต๋ถํด์ผ์ง !_!)
https://www.raywenderlich.com/books/combine-asynchronous-programming-with-swift
When should you use it?
1:N delegate ๊ด๊ณ๋ฅผ ๋ง๋ค ๋ ์ฌ์ฉํฉ๋๋ค.
์๋ฅผ ๋ค์ด์ ์ด ํจํด์ ์ฌ์ฉํ์ฌ, ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ฐ์ํ๋ ๊ฒฝ์ฐ ์ฌ๋ฌ ๊ฐ์ฒด์ ์๋ฆด ์ ์์ต๋๋ค. ๋ํ ๊ฐ delegate๋ ์ค์ค๋ก์ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๊ฑฐ๋, ์๋ต์ผ๋ก ๊ด๋ จ ์์ ์ ์ํํ ์๋ ์์ต๋๋ค.
Playground example
์ฐ์ MuticastDelegate helper class๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค!
// 1
public class MulticastDelegate<ProtocolType> {
// MARK: - DelegateWrapper
// 2
private class DelegateWrapper {
weak var delegate: AnyObject?
init(_ delegate: AnyObject) {
self.delegate = delegate
}
}
}
1. MulticaseDelegate์ ๋ชจ๋ ProtocolType์ ์ ๋ค๋ฆญ ํ์์ผ๋ก ํ์ฉํ๋ ์ ๋ค๋ฆญ ํด๋์ค๋ก ์ ์ํฉ๋๋ค. Swift๋ ์์ง <ProtocolType>์ ํ๋กํ ์ฝ๋ง์ผ๋ก ์ ํํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ง ์์ต๋๋ค. ๋ฐ๋ผ์ ProtocolType์ ๋ํ ํ๋กํ ์ฝ ๋์ ๊ตฌ์ฒด์ ์ธ ํด๋์ค ์ ํ์ ์ ๋ฌํ ์ ์์ต๋๋ค. ํ์ง๋ง ํ๋กํ ์ฝ์ ์ฌ์ฉํ ๊ฐ๋ฅ์ฑ์ด ๋์ผ๋ฏ๋ก ์ผ๋ฐ ์ ํ์ ์ด๋ฆ์ Type์ด ์๋ ProtocolType์ผ๋ก ์ง์ ํฉ๋๋ค!
2. DelegateWrapper๋ฅผ ๋ด๋ถ ํด๋์ค๋ก ์ ์ํฉ๋๋ค. ์ด ์ต์ ์ ์ฌ์ฉํ์ฌ, delegate ๊ฐ์ฒด๋ฅผ weak property๋ก ๋ฌถ์ ์ ์์ต๋๋ค. ์ด๋ฐ ๋ฐฉ์์ผ๋ก multicast delegate๋ strong warpper ์ธ์คํด์ค๋ฅผ ์ง์ delegatesํ์ง ์๊ณ ๋ ๋ณด์ ํ ์ ์์ต๋๋ค.
์ํ๊น๊ฒ๋ ์ฌ๊ธฐ์๋ delegate property์ ProtocolType๋์ AnyObject๋ก ์ ์ธํด์ผ ํฉ๋๋ค. ์๋ํ๋ฉด weak ๋ณ์๋ AnyObject(์ฆ, ํด๋์ค)์ด์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ฐ๋ฆฌ๋ ProtocolType์ด AnyObject๋ก์จ ์ฌ์ฉํ ์ ์๋ ์ ๋ค๋ฆญ ํ์ ์ด๋ผ๊ณ ์๊ฐํ ์ ์์ต๋๋ค. ํ์ง๋ง ํ๋ฅดํ ์ฝ ์์ฒด๋ฅผ ๊ฐ์ฒด๊ฐ ์๋ Type์ผ๋ก ์ ๋ฌํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์๋ํ์ง ์์ต๋๋ค.
// MARK: - Instance Properties
// 1
private var delegateWrappers: [DelegateWrapper]
// 2
public var delegates: [ProtocolType] {
delegateWrappers = delegateWrappers
.filter { $0.delegate != nil }
return delegateWrappers.map
{ $0.delegate! } as! [ProtocolType]
}
// MARK: - Object Lifecycle
// 3
public init(delegates: [ProtocolType] = []) {
delegateWrappers = delegates.map {
DelegateWrapper($0 as AnyObject)
}
}
1. DelegateWrapper๊ฐ DelegateWrapper ์ธ์คํด์ค๋ฅผ ๋ณด์ ํ๋๋ก ์ ์ธํฉ๋๋ค. DelegateWrapper ์ธ์คํด์ค๋ MulticastDelegate์ ์ํด์ ์ ๋ฌ๋ฉ๋๋ค.
2. delegate๋ฅผ ์ํ ์ฐ์ฐ ํ๋กํผํฐ๋ฅผ ์ถ๊ฐํฉ๋๋ค. ์ด๋ ๊ฒํ๋ฉด ํํฐ๋ง ํ ๋ค์์ nil์ด ์๋ delegate์ ๋ฐฐ์ด์ ๋ฐํํด์ค๋๋ค!
3. ๋ง์ง๋ง์ผ๋ก ์ด๋์ ๋ผ์ด์ ๋ฅผ ๋ง๋ญ๋๋ค!
์ด๋ฏธ ์์ ๋ ๋ฉํฐ์บ์คํธ delegate๋ฅผ ์ถ๊ฐ ๋ฐ ์ญ์ ํ๋ ์๋จ๋ ํ์ํฉ๋๋ค.
// MARK: - Delegate Management
// 1
public func addDelegate(_ delegate: ProtocolType) {
let wrapper = DelegateWrapper(delegate as AnyObject)
delegateWrappers.append(wrapper)
}
// 2
public func removeDelegate(_ delegate: ProtocolType) {
guard let index = delegateWrappers.firstIndex(where: {
$0.delegate === (delegate as AnyObject)
}) else {
return
}
delegateWrappers.remove(at: index)
}
1. ์ด๋ฆ์์ ์ ์ ์๋ฏ์ด delegate๋ฅผ addํ๋ ๋ฉ์๋์ ๋๋ค. delegate๋ฅผ AnyObject๋ก ๋ํํฉ๋๋ค.
2. ๋ง์ฐฌ๊ฐ์ง๋ก remove๋ฅผ ์ฌ์ฉํ์ฌ ์ ๊ฑฐํฉ๋๋ค!
๋ค์์ผ๋ก๋ ์ค์ ๋ก delegate๋ฅผ ํธ์ถํ๋ ์๋จ์ด ํ์ํฉ๋๋ค.
public func invokeDelegates(_ closure: (ProtocolType) -> ()) {
delegates.forEach { closure($0) }
}
์ฐ์ฐ ํ๋กํผํฐ๋ฅผ delegate๋ฅผ ์๋์ผ๋ก ํํฐ๋งํ๊ณ , ํด๋ก์ ๋ฅผ ์ ๋ฌ๋ ์์ฑ์ ํธ์ถํฉ๋๋ค.
์ฌ๊ธฐ๊น์ง๊ฐ MulticastDelegate helper class๊ฐ ์๊ฒผ๊ณ , ๋ค์ ์์ ์ ๋ณด์ฃ !
// MARK: - Delegate Protocol
public protocol EmergencyResponding {
func notifyFire(at location: String)
func notifyCarCrash(at location: String)
}
EmergencyResponding ํ๋กํฌํจ์ ์์ฑํฉ๋๋ค.
// MARK: - Delegates
public class FireStation: EmergencyResponding {
public func notifyFire(at location: String) {
print("Firefighters were notified about a fire at "
+ location)
}
public func notifyCarCrash(at location: String) {
print("Firefighters were notified about a car crash at "
+ location)
}
}
public class PoliceStation: EmergencyResponding {
public func notifyFire(at location: String) {
print("Police were notified about a fire at "
+ location)
}
public func notifyCarCrash(at location: String) {
print("Police were notified about a car crash at "
+ location)
}
}
๋๊ฐ์ delegate ๊ฐ์ฒด๋ฅผ ์ ์ํฉ๋๋ค. ๊ธด๊ธ์ํฉ์ด ๋ฐ์ํ๋ฉด ๊ฒฝ์ฐฐ๊ด๊ณผ ์๋ฐฉ๊ด์ด ๋ชจ๋ ๋์ํฉ๋๋ค.
// MARK: - Delegating Object
public class DispatchSystem {
let multicastDelegate =
MulticastDelegate<EmergencyResponding>()
}
MulticastDelegate์ ํ์ ์ผ๋กํ๋ ๋ณ์๋ฅผ ์ ์ธํฉ๋๋ค.
// MARK: - Example
let dispatch = DispatchSystem()
var policeStation: PoliceStation! = PoliceStation()
var fireStation: FireStation! = FireStation()
dispatch.multicastDelegate.addDelegate(policeStation)
dispatch.multicastDelegate.addDelegate(fireStation)
dispatch๋ฅผ DispatchSystem ์ธ์คํด์ค๋ก ์์ฑํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ policeStation ๋ฐ fireStation์ ๋ํ ์์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ dispatch.multicastDelegate.addDelegate(_:)๋ฅผ ํธ์ถํ์ฌ ๋ ๋ค ๋ฑ๋กํฉ๋๋ค.
dispatch.multicastDelegate.invokeDelegates {
$0.notifyFire(at: "Ray’s house!")
}
// console
// Police were notified about a fire at Ray's house!
// Firefighters were notified about a fire at Ray's house!
delegate๊ฐ 0์ด ๋๋ ๊ฒฝ์ฐ, multicast delegate์ ๋ํ ์ด๋ ํ ํฅํ ํธ์ถ์ ๋ํด์๋ ํต์ง๋๋ฉด ์๋ฉ๋๋ค.
print("")
fireStation = nil
dispatch.multicastDelegate.invokeDelegates {
$0.notifyCarCrash(at: "Ray's garage!")
}
// ์ฝ์
// Police were notified about a car crash at Ray's garage!
nil๋ก ์ฒ๋ฆฌํ์ฌ ์ฝ์์ ๋ํ๋์ง ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
What should you be careful about?
์ด ํจํด์ "information only" delegate ํธ์ถ์ ๊ฐ์ฅ ์ ํฉํฉ๋๋ค.
delegate๊ฐ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํด์ผ ํ๋ ๊ฒฝ์ฐ ์ด ํจํด์ด ์ ๋๋ก ์๋ํ์ง ์์ต๋๋ค. ์๋ํ๋ฉด ์ด ํจํด์ ๊ฒฝ์ฐ ์ฌ๋ฌ delegate๋ค์ด ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ ๋ณด๊ฐ ์ค๋ณต๋๊ฑฐ๋ ์ฒ๋ฆฌ๊ฐ ๋ญ๋น๋ ์๋ ์์ต๋๋ค.
์ด ๊ฒฝ์ฐ์๋ ๋ค์ ๋์์ธ ํจํด์์ ๋ค๋ฃจ๋ chain-of-responsibility pattern ์ฌ์ฉํ๋ ๊ฒ์ ๊ณ ๋ คํ์ธ์ค!
Tutorial project
๋ฉํฐ ์บ์คํธ ํจํด์ ๋๋ค! ์ด๋ ต์ง ์์์!
https://github.com/lgvv/DesignPattern/tree/main/multicast-delegate-pattern
Key points
์ด๋ฒ ์๊ฐ์๋ multicast delegate pattern์ ๋ํด์ ์์๋ณด์์ต๋๋ค.
- ๋ฉํฐ์บ์คํธ ๋๋ฆฌ๊ฒ์ดํธ ํจํด์ ์ฌ์ฉํ๋ฉด 1:N delegate ๊ด๊ณ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ฌ๊ธฐ์๋ 4๊ฐ์ง ์ ํ์ด ์์ต๋๋ค.
(์์ ์ด๋ฏธ์ง ์ฐธ๊ณ )
- object needing a delegate์๋ ํ๋ ์ด์์ delegates๊ฐ ์์ต๋๋ค. delegate๋ ๊ตฌํํด์ผ ํฉ๋๋ค. delegates๋ ํ๋กํ ์ฝ์ด๊ณ , multicast delegate๋ delegate๋ฅผ ๋ณด์ ํ๊ณ ์๋ helper class์ ๋๋ค.
- Swift์์๋ ์ง์ ์ ์ผ๋ก multicast delegate ๊ฐ์ฒด๋ฅผ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด ํจํด์ ์ํด ์ง์ ๊ตฌํํ๋ ๊ฒ์ ์ฝ์ต๋๋ค.
'apple > ๐ก DesignPattern & Architecture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift] FlyWeight Pattern (1) | 2022.06.09 |
---|---|
[Swift] Facade Pattern (1) | 2022.06.07 |
[Swift] State Pattern (0) | 2022.05.30 |
[Swift] Prototype Pattern (0) | 2022.05.28 |
[Swift] Iterator Pattern (0) | 2022.05.13 |