iOS设计模式解析

观察者模式

2019-08-02  本文已影响0人  FY_Chao

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"

设计原则

上一篇下一篇

猜你喜欢

热点阅读