swift設計模式:(二)觀察者模式(Observe Pattern)

觀察者模式

定義:觀察者設計模式定義了對象間的一種一對多的依賴關係,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並且自動刷新。
設計原則:爲了交互對象之間的松耦合設計而努力。

舉例說明:

比如老闆在一個辦公室裏開會,辦公室裏有部分員工,在辦公室的員工就是Observer(觀察者),正在開會的老闆就是Subject(主題:負責發送通知—Post Notification)。如果其他員工也想成爲Observer,那麼必須得進入(addObserver)正在開會的會議室成爲觀察者。員工成功觀察者後收到通知得做一些事情吧(doSomething),比如記個筆記神馬的。如果此時員工鬧情緒,不想聽老闆開會了,於是通過removeObserver走出了會議室。上面這個過程其實就是觀察者模式。

一、通過示例認識觀察者模式

場景:使用boss發送通知,員工接受通知

boss和員工顯然是一對多的關係,我們將要實現的“類圖”如下所示:
在這裏插入圖片描述

1、圖中的SubjectType是 通知者的基類,負責發通知

參數 說明
info 表示發佈的信息
observerArray 保存多個觀察者的數組,使用數組的原因是由於 Subject:Observers是一對多的關係
函數 說明
registerObserver(observer) 註冊觀察者
removeObserver(observer) 移除觀察者
notigyObserver() 通知觀察者

圖中Boss是SubjectType的子類,繼承了父類的所有的所有屬性,並重寫了父類的三個方法

函數 說明
setInfo() 負責更新Info信息的時候調用發出通知的方法

2、圖中ObserverType 是 觀察者的基類

參數 說明
info 存儲接收到的通知信息
函數 說明
update() 接收到通知後要執行的方法,即boss一發通知,員工就會執行該方法
display() 對info字段的信息進行輸出

注:把SubjectType 和 ObserverType設計成基類,不利於後期的擴展,或者會在擴展中會產生重複的代碼,可以使用接口或者結合其他設計模式,能很好的解決上面的問題。

3、類圖的具體實現

上面的Boss負責發通知,Coder 和PM負責監聽 Boss發的通知

1)通知者和觀察者基類的實現

在SubjectType基類中的observerArray中存儲的是ObserverType類型(包括其子類)對象, 即 所有的觀察者

//基類
class ObserverType{
    
    //發佈的消息
    var info : String = ""
    func update(_ info : String){}
    func display(){}
}
class SubjectType{
    var observersArray : [ObserverType] = [ObserverType]()
    var info : String = ""
    //註冊觀察者
    func registerObserver(_ observer : ObserverType){}
    //移除觀察者
    func removeObserver(_ observer : ObserverType){}
    //通知觀察者
    func notifyObserver(){}
}

2)負責發送通知的Boss類的實現

Boss 繼承自SubjectType 。
當Boss 執行 setInfo() 方法時,就會調用 notifyObservers()進行通知的發送。
在Boss中的registerObserver()方法用來添加監聽者(爲了防止重複添加,我們在添加前先進行移除),removeObserver() 則是複雜移除監聽者,notifyObservers() 是發送通知並調用觀察者相應的方法。

//發出通知的人,也就是通知中心,大Boss
class Boss : SubjectType{
    func setInfo(_ info : String){
        if info != self.info {
            self.info = info
            self.notifyObserver()
        }
    }
    
    override func registerObserver(_ observer: ObserverType) {
        self.removeObserver(observer)
        self.observersArray.append(observer)
    }
    
    override func removeObserver(_ observer: ObserverType) {
        for i in 0..<self.observersArray.count {
            if type(of: observersArray[i]) == type(of: observer){
                self.observersArray.remove(at: i)
                break
            }
        }
    }
    override func notifyObserver() {
        for observer : ObserverType in self.observersArray {
            observer.update(self.info)
        }
    }
}

3)觀察者的實現

有兩個觀察者,分別是 Coder(程序員) 和PM(產品經理),都是ObserverType的子類,重寫了 基類的 update()和display()方法。
觀察者在觀察到 Subject 的info 被改變後,就會執行 update()方法。

//程序員
class Coder : ObserverType{
    override func update(_ info: String) {
        self.info = info
        display()
    }
    override func display() {
        print("程序員收到:\(self.info)")
    }
}
//產品經理
class PM : ObserverType{
    override func update(_ info: String) {
        self.info = info
        display()
    }
    override func display() {
        print("產品經理收到:\(self.info)")
    }
}

4)測試用例

//創建boss
    let bossSubject : Boss = Boss()
    //創建觀察者
    let coderObserver : Coder = Coder()
    let PMObserver : PM = PM()
    //添加觀察者
    bossSubject.registerObserver(coderObserver)
    bossSubject.registerObserver(PMObserver)
    print(bossSubject.observersArray)
    bossSubject.setInfo("第一次通知")
    //程序員走出了會議室(移除通知)
    bossSubject.removeObserver(coderObserver)
    bossSubject.setInfo("第二次通知")

測試結果爲:
第一次發生通知的時候,兩個都是觀察者,接着 移除了coder的觀察者,第二次發通知的時候,就只有pm是觀察者了。
在這裏插入圖片描述

二、Foundation 框架中的通知

Foundation框架是自帶一套完成的 觀察者模式機制的, 即 通知機制。

1、NotificationCenter 簡述

NotificationCenter是Foundation框架通知機制中的通知中心,扮演者調度通知的作用。
Subject 往通知中心發通知,有通知中心統一管理,把發送的消息分發給相應的觀察者。
所以通知中心是一個集合,集合中有多個 Subject 和多個 Observer的集合。
通知中心扮演的角色就是將Subject 和 相應的 Observer進行關聯。
原理圖如下所示:
在這裏插入圖片描述

2、Foundation 中通知的使用

1)創建 Subject 通知者
有以下幾個步驟:
(1)創建消息字典,該字典就是觀察者所獲取的信息
(2)創建通知 Notification,該通知也是需要發送給 Observer的,通知中包括 Subject 的名稱、發送通知的對象,以及創建的消息字典
(3)將 通知 發送給 通知中心 NotificationCenter ,通知中心會根據 通知的信息找到觀察此通知的 觀察者Observer,並把通知傳給每個觀察者

Boss扮演的就是Subject

//subject
class Boss : NSObject{
    func sendMessage(_ message : String){
        //1、創建消息字典
        let userInfo = ["message":message]
        //2、創建通知
        let notification = Notification.init(name: Notification.Name(rawValue: "Boss1"), object: nil, userInfo: userInfo)
        //3、發送通知
        NotificationCenter.default.post(notification)
    }
}

2)添加 Observer 觀察者
我們需要制定觀察者所觀察的 Subject,通過其名稱來明確觀察的對象,還需要指定收到通知後的執行方法,在指定的方法有一個參數,是用來接收發出的 通知Notification 的對象的。
注意:在當前對象釋放時,需要移除觀察者

//添加observer
class Coder:NSObject{
    func observerBoss(){
        NotificationCenter.default.addObserver(self, selector: #selector(accepteNotification(_:)), name: NSNotification.Name(rawValue: "Boss"), object: nil)
    }
    @objc func accepteNotification(_ notification : Notification){
        let info = notification.userInfo
        print("收到老闆通知了:\(String(describing: info!["message"]))")
    }
    deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: "Boss"), object: nil)
    }
}

3)測試用例

let boss = Boss()
    let coder = Coder()
    coder.observerBoss()
    boss.sendMessage("漲工資啦")

測試結果爲在這裏插入圖片描述

三、自定義通知中心

實現一套屬於自己的通知機制,調用方式類似於Foundation框架中的通知調用方式,只是命名不同。

1、分析通知機制原理

自定義通知機制的 類“類圖”如下所示:
在這裏插入圖片描述
1)MyCustomNotificationCenter 對應 NotificationCenter
MyCustomNotification 對應 Notification
MySubject 和MyObserver 是爲了實現該通知機制所創建的Subject 和Observer

2)通知機制的運作方式
Boss將Notification發送到NotificationCenter,然後NotificationCenter在通過其內部實現機制,將Boss發送過來的Notification發送到Coder。

3)MyCustomNotification

參數 說明
name 發送通知對象的名稱 ,即“Boss”
object 上述例子的Boss的對象
userInfo 發送給Observer的信息字典

4)MyObserver

參數 說明
observer 觀察者對象
selector 觀察者對象收到通知後要執行的方法

5)MySubject

參數 說明
notification 記錄要發送的通知
observers 是一個數組,存儲該subject對應的觀察者
函數 說明
addCustomObserver 添加觀察者
removeCustomObserver 移除觀察者
postNotification 發送通知

6)MyCustomNotificationCenter
該類的對象是通過 defaultCenter() 方法獲取的單例對象,在該方法中有一個 center 名稱的字段,該字段是字典類型, key 對應MySubject 對象指定的name,value對應 Mysubject 對象。
其中也有添加、移除觀察者和發送通知等方法。

2、Subject 和 Observer 實現

1)MyCustomNotification

參數 說明
name 發送通知對象的名稱 ,即“Boss”
object 上述例子的Boss的對象
userInfo 發送給Observer的信息字典
//通知
class MyCustomNotification:NSObject{
    let name : String
    let object : AnyObject?
    let userInfo : [NSObject : AnyObject]?
    
    init(_ name : String, _ object : AnyObject?, _ userInfo : [NSObject:AnyObject]?) {
        self.name = name
        self.object = object
        self.userInfo = userInfo
    }
}

2)MyObserver

參數 說明
observer 觀察者對象
selector 觀察者對象收到通知後要執行的方法

當收到通知時,就會執行observer的selector方法。

//觀察者
class MyObserver:NSObject{
    let observer : AnyObject
    let selector : Selector
    
    init(_ observer : AnyObject, selector : Selector){
        self.observer = observer
        self.selector = selector
    }
}

3)MySubject
MySubject類將Notification與Observers進行關聯。
具體說來就是當MySubject收到Notification中,就會遍歷其所有的觀察者(observers的類型是ObserveArray,其中存儲的是MyObserver的對象),遍歷觀察者時就會去執行該觀察者所對應的selector方法。

參數 說明
notification 存儲的就是Subject所要發出的通知
observers 數組類型,其中存儲的是MyObserver的對象
函數 說明
addCustomObserver() 往observers數組中添加觀察者
removeCustomObserver() 移除observers數組中的觀察者
postNotification() 對observers數組進行遍歷取出MyObserver的對象,然後執行該對象中的selector方法,並且將notification作爲selector方法的參數
typealias ObserverArray = Array<MyObserver>
//主題
class MySubject : NSObject{
    var notification : MyCustomNotification?
    var observers : ObserverArray
    
    init(_ notification : MyCustomNotification?, _ observers : ObserverArray){
        self.notification = notification
        self.observers = observers
    }
    
    //添加觀察者
    func addCustomObserver(_ observer : MyObserver){
        for i in 0..<observers.count {
            if observers[i].observer === observer.observer{
                return
            }
        }
        self.observers.append(observer)
    }
    
    //移除觀察者
    func removeCustomObserver(_ observer : MyObserver){
        for i in 0..<observers.count {
            if observers[i].observer === observer.observer{
                observers.remove(at: i)
                break
            }
        }
    }
    
    //發送通知
    func postNotification(){
        for i in 0..<observers.count {
            let myObserver : MyObserver = self.observers[i]
            myObserver.observer.performSelector(inBackground: myObserver.selector, with: self.notification)
        }
    }
}

3、通知中心的實現

通知中心的類的定義如下:

typealias SubjectDictionary = Dictionary<String, MySubject>
class MyCustomNotificationCenter : NSObject{
}

1)模擬系統通知中心的單例獲取

private static let singleton = MyCustomNotificationCenter()
    static func defaultCenter()->MyCustomNotificationCenter{
        return singleton
    }
    override init() {
        super.init()
    }

2)通知中心發送通知的過程

對應NotificationCenter.defaultCenter中的postNotification(notification)。

(1)首先會調用getSubjectWithNotifaction(notification)方法來從center中獲取可以發送該notification的Subject對象
(2)在getSubjectWithNotifaction(notification)中,如果center中沒有可以發送該notification的對象,那麼就創建一個MySubject對象,並將該notification賦值給這個新建的MySubject對象,最後將我們創建的這個新的subject添加進center數組中
(3)然後調用該subject對象的postNotification()方法即可

//================通知中心,存儲的是Subject對象的集合============
    private var center : SubjectDictionary = SubjectDictionary()
    
    //發出通知
    func postNotification(_ notification : MyCustomNotification){
        //首先會調用getSubjectWithNotifaction(notification)方法來從center中獲取可以發送該notification的Subject對象
        let subject = self.getSubjectNotification(notification)
        //然後調用該subject對象的postNotification()方法即可
        subject.postNotification()
    }
    
    //根據notification獲取相應的subject對象,沒有的話就創建
    func getSubjectNotification(_ notification : MyCustomNotification)->MySubject{
        
        guard let subject = center[notification.name] else {
            //如果center中沒有可以發送該notification的對象,那麼就創建一個MySubject對象,並將該notification賦值給這個新建的MySubject對象,
            //最後將我們創建的這個新的subject添加進center數組中
            center[notification.name] = MySubject(notification, ObserverArray())
            return self.getSubjectNotification(notification)
        }
        if subject.notification == nil {
            subject.notification = notification
        }
        return subject
    }

3)添加監聽者

對應NotificationCenter.defaultCenter中的addObserver()。

(1)首先我們把傳入的參數生成MyObserver的對象,
(2)然後通過aName從center字典中獲取相應的MySubject對象。如果center中沒有對應的MySubject對象,我們就創建該對象,並且將該對象的notification屬性暫且指定爲nil。
(3)最後調用MySubject類中的addCustomObserver()方法進行觀察者的添加。

//添加監聽者
    func addObserver(_ observer : AnyObject, _ aSelector : Selector, _ aName : String){
        //首先我們把傳入的參數生成MyObserver的對象
        let myObserver = MyObserver(observer, selector: aSelector)
        //通過aName從center字典中獲取相應的MySubject對象
        var subject : MySubject? = center[aName]
        
        //如果center中沒有對應的MySubject對象,我們就創建該對象,並且將該對象的notification屬性暫且指定爲nil
        if subject == nil {
            subject = MySubject(nil, ObserverArray())
            center[aName] = subject
        }
        
        //調用MySubject類中的addCustomObserver()方法進行觀察者的添加
        subject?.addCustomObserver(myObserver)
    }

4)移除監聽者

(1)首先也是通過name從center字典中獲取MySubject的對象,(2)然後調用MySubject對象的removeCustomObserver()方法進行移除掉。

//從通知中心移除observer
    func removeObserver(_ observer : AnyObject, _ name : String){
        //首先也是通過name從center字典中獲取MySubject的對象
        guard let subject : MySubject = center[name] else {
            return
        }
        //然後調用MySubject對象的removeCustomObserver()方法進行移除掉
        subject.removeCustomObserver(observer as! MyObserver)
    }

4、測試用例

class Boss : NSObject{
    func sendMessage(_ message : String){
        //1、創建消息字典
        let userInfo = ["message":message]
        //2、創建通知
        let notification = MyCustomNotification.init(Notification.Name(rawValue: "Boss2").rawValue, nil, userInfo as [NSObject : AnyObject])
        //3、發送通知
        MyCustomNotificationCenter.defaultCenter().postNotification(notification)
    }
}
//添加observer
class Coder:NSObject{
    func observerBoss(){
        MyCustomNotificationCenter.defaultCenter().addObserver(self, #selector(accepteNotification(_:)), "Boss2")
    }
    @objc func accepteNotification(_ notification : MyCustomNotification){
        let info : Dictionary = notification.userInfo!
        print("收到老闆通知了:",info["message" as! NSObject])
    }
    deinit {
        MyCustomNotificationCenter.defaultCenter().removeObserver(self, "Boss2")
    }
}

let boss = Boss()
    let coder = Coder()
    let coder = Coder()
    coder.observerBoss()
    coder.observerBoss()
    boss.sendMessage("漲工資啦")

測試結果與二中測試結果一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章