观察者模式

作者: FY_Chao | 来源:发表于2019-08-02 16:15 被阅读0次

    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"
    

    设计原则

    • 为了交互对象之间的松耦合而努力。

    相关文章

      网友评论

        本文标题:观察者模式

        本文链接:https://www.haomeiwen.com/subject/yfykdctx.html