I read the Head First Design Patterns book and try to implement observer pattern in swift language. The code is below and the scenario is discussed after that.
protocol Observer:AnyObject{
func update(temp: Float, humidity: Float, pressure: Float)
}
protocol Subject{
func registerObserver(obs: Observer)
func removeObserver(obs: Observer)
func notifyObservers()
}
protocol DisplayElement{
func display()
}
class WeatherData: Subject{
private var observers = [Observer]()
private var temp: Float = 0.0
private var humidity: Float = 0.0
private var pressure: Float = 0.0
func registerObserver(obs: Observer) {
observers.append(obs)
}
func removeObserver(obs: Observer) {
let index = observers.firstIndex { $0 === obs }
guard let unIndex = index else {
return
}
observers.remove(at: unIndex)
}
func notifyObservers() {
observers.forEach { obs in
obs.update(temp: temp, humidity: humidity, pressure: pressure)
}
}
func measurementsChanged() {
notifyObservers()
}
func setMeasurements(temp: Float, humidity: Float, pressure: Float){
self.temp = temp
self.humidity = humidity
self.pressure = pressure
self.measurementsChanged()
}
}
class CurrentConditionsDisplay{
private var temp: Float = 0.0
private var humidity: Float = 0.0
private let weatherObj: WeatherData
init(wea: WeatherData) {
self.weatherObj = wea
self.weatherObj.registerObserver(obs: self)
}
}
extension CurrentConditionsDisplay: DisplayElement{
func display() {
print("Current Temperature = \(self.temp) and Humidity = \(self.humidity)")
}
}
extension CurrentConditionsDisplay: Observer{
func update(temp: Float, humidity: Float, pressure: Float) {
self.temp = temp
self.humidity = humidity
self.display()
}
}
Here, the scenario is you have a weather station which provide weather information in your WeatherData
class. You job is, when WeatherData
class changes a new state you just need to notify several devices who are observing or subscribing for your information. The weather station class is.
class weatherStation{
func test() {
let weatherData = WeatherData()
_ = CurrentConditionsDisplay(wea: weatherData)
_ = StatisticsDisplay(wea: weatherData)
_ = ForecastDisplay(wea: weatherData)
weatherData.setMeasurements(temp: 80, humidity: 60, pressure: 30)
weatherData.setMeasurements(temp: 70, humidity: 60, pressure: 30)
weatherData.setMeasurements(temp: 50, humidity: 60, pressure: 30)
}
}
Here I am pushing fixed data into observer
classes. Letter they introduce that we can pull the data which we need instead of pushing fixed data to every observer classes. Our(publisher) task is just to notify the observers that our state changes and pass ourself as a function argument. Then they(observer) use our getter methods whichever data they needed. Something like that.
func update(obs: Subject, arg: [String:Any])
It's ok to pass publisher object as argument but we can avoid this because every observer classes already have same related publisher object(private let weatherObj: WeatherData
) which we pass. So why we pass it which is already have in our observer classes? Why not just use them? Is there any bad practice if you use them? Shouldn't I use this declaration inside observer classes as weak reference(private let weatherObj: WeatherData)?
NB:- I know about key value observer and push notification. The question is for clear the concept.