觀察者模式
定義:觀察者設計模式定義了對象間的一種一對多的依賴關係,以便一個對象的狀態發生變化時,所有依賴於它的對象都得到通知並且自動刷新。
設計原則:爲了交互對象之間的松耦合設計而努力。
舉例說明:
比如老闆在一個辦公室裏開會,辦公室裏有部分員工,在辦公室的員工就是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("漲工資啦")
測試結果與二中測試結果一致。