There are scenarios when two interacting sets of generic classes is a good design pattern. A simple example is the Observable-Observer pattern. The observable posts events to the observer but the pattern is the same no matter what type of event that is being observed.
My first thought was that the preferred way would be to define two generic protocols. This should offer the smallest coupling, which tends to be good as the code base grows.
protocol ProtocolObserver {
typealias EventType
func update<O:ProtocolObservable where O.EventType == EventType>(observable:O, event:EventType) -> Void
}
protocol ProtocolObservable {
typealias EventType
func registerObserver<O:ProtocolObserver where O.EventType == EventType>(observer:O) -> Bool
func unregisterObserver<O:ProtocolObserver where O.EventType == EventType>(observer:O) -> Void
}
Trying to define classes that implement the above protocols turned out to be a world of hurt. I failed to find any way to do it.
Implementing a generic baseclass would, however, be an acceptable solution.
protocol GenericObserver {
func update<EventType>(observable:GenericObservable<EventType>, event:EventType);
}
class GenericObservable<EventType> {
private var observers:[GenericObserver] = []
func registerObserver(observer:GenericObserver) -> Bool {
// Code to avoid registering the same observer twice
observers.append(observer)
return true
}
func unregisterObserver(observer:GenericObserver) -> Void {
// Code to remove the observer if present in observers
}
func notifyObservers(event:EventType) -> Void {
for observer in observers {
observer.update(self, event: event)
}
}
}
No problem defining some classes that implement the protocol this time. Adding them to an instance of the generic observer did not show the behaviour I expected.
let numberObservable = GenericObservable<NSNumber>()
class NumberObserver : GenericObserver {
func update<NSNumber>(observable:GenericObservable<NSNumber>, event:NSNumber) {
print("Number Event \(event)")
}
}
let numberObserver = NumberObserver()
numberObservable.registerObserver(numberObserver)
class DataObserver : GenericObserver {
func update<NSData>(observable:GenericObservable<NSData>, event:NSData) {
print("Data Event \(event)")
}
}
let dataObserver = DataObserver()
numberObservable.registerObserver(dataObserver)
numberObservable.notifyObservers(NSNumber(int: 42))
I expected numberObservable.registerObserver(dataObserver) to cause a compilation error. Instead it happily printed the output
Number Event 42
Data Event 42
This all leaves me with two questions:
What have I misunderstood when I expect the compiler to not accept
numberObservable.registerObserver(dataObserver)?Is there a way to implement a pair of classes that conform to
ProtocolObserverandProtocolObservablerespectively?