观察者模式

What 观察者模式

我们可以先通过生活上的事情来认识观察者模式,在日常生活中订阅报纸就可以看成是观察者模式的实现。

  • 我们首先向某家报社订阅报纸。
  • 当报社出版新的报纸后,就会送到你的手上。
  • 当你不想看报纸时,你可以向报社取消订阅,他们就不会在送新的报纸。
  • 只要报社一直存在,就会一直有人向报社订阅或者取消订阅报纸。

上面的 「订阅者」 +「报社」= 观察者模式,然后我们抽象一下 「订阅者」 和「报社」我们就可以得到下面的一张图:

模式定义: 观察者模式定义了对象之间的一对多关系,这样一来,当一个对象发生变化时,它的所有观察者都会收到通知并自动更新。

实现观察者模式的方法不止一种,但是以接口(协议)实现的最为常见。如图中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

GitHub源码——Observe02

在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"

设计原则

  • 为了交互对象之间的松耦合而努力。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • 什么是观察者模式?我们先打个比方,这就像你订报纸。比如你想知道美国最近放生了些新闻,你可能会订阅一份美国周刊,然后...
    泥孩儿0107阅读 676评论 0 0
  • 在正式介绍观察者模式前,我们先引用生活中的小例子来模拟观察者,先对观察者模式有一个整体的感觉。 现实模拟 报纸和杂...
    六尺帐篷阅读 1,643评论 6 18
  • 观察者(Observer)模式 走进观察者模式 首先,先思考订阅报纸是怎么回事报社的业务就是出版报纸,并把报纸送给...
    廖少少阅读 409评论 3 0
  • 1.初识观察者模式 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被...
    王侦阅读 867评论 0 1
  • 偶听樱花,烟雨绝佳,梦中曾会,残红犹过夕霞,待风起,满山流彩,销魂此处最华。春雨嫌寂寞,偏洒处,嫩叶思发,引...
    寒烟洛水阅读 287评论 0 1