What 观察者模式
我们可以先通过生活上的事情来认识观察者模式,在日常生活中订阅报纸就可以看成是观察者模式的实现。
- 我们首先向某家报社订阅报纸。
- 当报社出版新的报纸后,就会送到你的手上。
- 当你不想看报纸时,你可以向报社取消订阅,他们就不会在送新的报纸。
- 只要报社一直存在,就会一直有人向报社订阅或者取消订阅报纸。
上面的 「订阅者」 +「报社」= 观察者模式,然后我们抽象一下 「订阅者」 和「报社」我们就可以得到下面的一张图:
![](https://img.haomeiwen.com/i1178839/705b6ec4f7213ebe.png)
模式定义: 观察者模式定义了对象之间的一对多关系,这样一来,当一个对象发生变化时,它的所有观察者都会收到通知并自动更新。
实现观察者模式的方法不止一种,但是以接口(协议)实现的最为常见。如图中ConcreteSubject
管理着某些数据,而当这些数据发生改变时,ConcreteSubject
就会通知观察者们 ConcreteObserve
,这些ConcreteObserve
就会根据收到的数据进行更新。其中添加、删除、通知的操作都是通过接口(Subject
协议)实现的。而收到数据后的更新也是通过接口(Observe
协议)实现的。
在这里我们使用观察者模式来实现了主题和观察者间的松耦合。任何时候我们可以不修改主题代码就可以对观察者都可以进行增加、删除、修改等操作,只需要保证这些观察者是遵守了Observe
协议。主题和观察者间只要它们遵守了协议,它们内部不管如何修改都不会影响到外部。
例子1
GitHub源码——Observe01
实现一个气象站的设计,气象站监测三个数据温度、湿度、气压。我们通过这三个数据生成三种(或者有可能多种)气象看板分别是目前状况、气象统计、天气预报。当气象站的数据发生改变时,我们需要同时刷新三个看板的数据。
// MARK: 面向协议编程
/// 展示数据
public protocol DisplayElement {
func display()
}
public protocol Observer {
/// 更新数据
func update(temp: Float?, humidity: Float?, pressure: Float?)
}
public protocol Subject {
/// 对观察者操作
func registerObserver(o: Observer)
func removeObserver(o: Observer)
func notifyObserves()
}
// Subject的代码,遵守 Subject 管理这所有的Observe
class WeahterData: Subject {
var observers: [Observer] = []
private var temperature: Float?
private var humidity: Float?
private var presure: Float?
func measurementsChanged() {
notifyObserves()
}
func setMeasurements(temperature: Float?, humidity: Float?,presure: Float?) {
self.temperature = temperature
self.humidity = humidity
self.presure = presure
measurementsChanged()
}
// MARK: -
// MARK: Subject Protocol
func registerObserver(o: Observer) {
observers.append(o)
}
func removeObserver(o: Observer) {
}
func notifyObserves() {
for observer in observers {
observer.update(temp: temperature, humidity: humidity, pressure: presure)
}
}
}
/// 其中的一个观察者,遵守了 Observer, DisplayElement
// MARK:- 公告板类
class CurrentConditionsDisplay: Observer, DisplayElement {
private var temperature: Float?
private var humidity: Float?
private var weatherData: Subject
init(_ weatherData: Subject) {
self.weatherData = weatherData
weatherData.registerObserver(o: self)
}
func update(temp: Float?, humidity: Float?, pressure: Float?) {
self.temperature = temp
self.humidity = humidity
display()
}
func display() {
print("Current Conditions: temperature \(String(describing: temperature)) humidity \(String(describing: humidity)) ")
}
}
例子2
在iOS中与OC一样,Swift实现观察者模式的主要有两种技术——通知和KVO。
通知
/// 模型对象内部数据发生改变时,发送通知
let notification = Notification(name: Notification.Name(rawValue: "data changes") , object: self, userInfo: nil)
NotificationCenter.default.post(notification)
/// 订阅通知,
NotificationCenter.default.addObserver(self, selector: #selector(dataChange(_:)), name: NSNotification.Name("data changes"), object: nil)
/// 监听数据发生变化时调用
@objc func dataChange(_ noti:Notification) {
}
KVO
KVO通过遵守非正式协议 NSKeyValueObserving来实现,因为NSObject
提供NSKeyValueObserving协议的默认实现,Swift使用KVO的类需要是继承自NSObject
的,
/// 使用@objc属性和dynamic修改器标记要通过键值观察观察的属性。
class MyObjectToObserve: NSObject {
@objc dynamic var myDate = NSDate(timeIntervalSince1970: 0) // 1970
func updateDate() {
myDate = myDate.addingTimeInterval(Double(2 << 30)) // Adds about 68 years.
}
}
/// 定义观察者
class MyObserver: NSObject {
@objc var objectToObserve: MyObjectToObserve
var observation: NSKeyValueObservation?
init(object: MyObjectToObserve) {
objectToObserve = object
super.init()
observation = observe(
\.objectToObserve.myDate,
options: [.old, .new]
) { object, change in
print("myDate changed from: \(change.oldValue!), updated to: \(change.newValue!)")
}
}
}
/// 值变更
let observed = MyObjectToObserve()
let observer = MyObserver(object: observed)
observed.updateDate() // 触发观察者
// Prints "myDate changed from: 1970-01-01 00:00:00 +0000, updated to: 2038-01-19 03:14:08 +0000"
设计原则
- 为了交互对象之间的松耦合而努力。
网友评论