定義
觀察者模式有主題和觀察者,主題可以訂閱多個觀察者,當主題發生變化時,通知觀察者來做相應改變。
UML圖
Paste_Image.png
定義了一個主題接口,可添加,刪除,通知觀察者。
定義了一個觀察者接口,在收到主題變化通知后,做出變化。
實現例子
拿Head First上面的氣象站的例子來說吧。
需要實現的功能大致是這樣:
根據氣象站的測量接口數據的變化(有個WetherData的類來提供數據),顯示天氣數據的board必須立即更新。有“目前狀況”,“氣象統計”,“天氣預報”三塊board。系統必須可擴展,可隨意添加/刪除board。
其實,這就比較適合用觀察者模式,WetherData為subject,當有更新時,通知到board,來更新數據顯示。
代碼如下:
觀察者,主題接口
// 主題接口
protocol ISubject {
func addObserver(observer: BaseObserver)
func removeObserver(observer: BaseObserver)
func notifyObservers()
}
// 觀察者接口
protocol IObserver {
func update(subject: ISubject)
}
class BaseObserver: IObserver {
var value: Int = {
let now = NSDate()
let timeInterval = now.timeIntervalSince1970 * 100000
let interval: Int = Int(timeInterval)
print(interval)
return interval
}()
func update(subject: ISubject) {
fatalError("subclass should implement this method")
}
}
extension BaseObserver: Equatable, Hashable {
var hashValue: Int {
return self.value
}
}
func ==(lhs: BaseObserver, rhs: BaseObserver) -> Bool {
return lhs.hashValue == rhs.hashValue
}
本來只想定義個IObserver,addObserver(observer: IObserver),但是
removeObserver(observer: IObserver)中,swift沒有像oc那樣removeObject的方法,只得自己實現Equatable, Hashable來判斷是否是同一個對象,來進行刪除。所以這里就采用了定義基類BaseObserver,采用時間hash,讓其具有唯一性。
如果有其他好的方法歡迎指出??
WeatherData
class WeatherData: ISubject {
private var observers: [BaseObserver] = []
private var temperature: Float = 0
private var humidity: Float = 0
private var pressure: Float = 0
func addObserver(observer: BaseObserver) {
observers.append(observer)
}
func removeObserver(observer: BaseObserver) {
observers = observers.filter({
$0 != observer
})
}
func notifyObservers() {
for observer in observers {
observer.update(self)
}
}
func updateMeasurements(temperature: Float, _ humidity: Float, _ pressure: Float) {
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
self.notifyObservers()
}
// 溫度
func getTemperature() -> Float {
return self.temperature
}
// 濕度
func getHumidity() -> Float {
return self.humidity
}
// 氣壓
func getPressure() -> Float {
return self.pressure
}
}
當前狀態board,其他的board類似
// 目前狀況
class CurrentConditionDisplay: BaseObserver {
init(subject: ISubject) {
super.init()
subject.addObserver(self)
}
override func update(subject: ISubject) {
if let data = subject as? WeatherData {
let temperature = data.getTemperature()
let humidity = data.getHumidity()
let pressure = data.getPressure()
print("temperature:\(temperature), humidity:\(humidity), pressure:\(pressure)")
}
}
}
最終調用:
// observer
let weatherData = WeatherData()
let currentDisplay = CurrentConditionDisplay(subject: weatherData)
let statsticsDisplay = StatsticsDisplay(subject: weatherData)
let forcastDisplay = ForecastDisplay(subject: weatherData)
weatherData.updateMeasurements(10, 1, 200)
輸出:
temperature:10.0, humidity:1.0, pressure:200.0
最大氣壓為:200.0
明天的天氣為晴,5~10°,無風
若加上weatherData.removeObserver(currentDisplay)
則不會輸出當前狀態
最大氣壓為:200.0
明天的天氣為晴,5~10°,無風