NotificationCenter 協議

讓觀察者模式變得更美好

OSX 已經有至少 17 年的歷史,而NotificationCenter在其第一次版本發布就已經存在,并且一直是蘋果開發者常用的工具。對于不了解的人來說,NotificationCenter 是基于觀察者模式的概念,也是軟件設計模式中行為型模式的一部分。

觀察者模式

觀察者模式由Gang of Four在 90 年代中期提出并一直存在,是一種比較容易理解的設計模式。首先,會存在一個被稱之為觀察目標的對象;這個對象維護一個包含觀察者的列表,并將狀態的變化通知給這些觀察者。

舉個真實的例子。你所在的城市有一家繁忙的咖啡店。不少顧客在排隊買咖啡,咖啡師會詢問顧客的姓名,并將其寫在杯子上,以便分清楚咖啡是誰點的;然后讓顧客禮貌地等待其名字被叫。每制作完一杯咖啡,咖啡師會叫出杯子上所寫的名字,從而讓顧客愉快地取到自己所點的咖啡。

在這種情況下,咖啡師是觀察目標,購買咖啡的顧客是觀察者,而咖啡是狀態的變化,因為咖啡從一個空杯變成了滿滿一杯含咖啡因的美味。

NotificationCenter的問題

對于寫代碼的我們,觀察者模式毫無疑問是一種有很多用途的偉大模式。但同時不得不承認,我從來不是它的狂熱粉絲,并非因為缺乏一些好的理由:

保證觀察對象的一致性

如果一個項目中沒有強制性的標準,那么實現和向觀察者發送通知的方式可能就會多種多樣。例如混亂的通知名稱:

classBarista{

letnotification ="coffeeMadeNotification"

}

classTrainee{

letcoffeeMadeNotificationName ="Coffee Made"

}

避免通知名稱沖突

如果開發者隨意給通知起名,那么兩個不同的觀察對象則可能擁有相同的通知名,于是無論這兩者誰發出一個采用此名字的通知,錯誤的觀察者便可能會收到此通知。

假設咖啡店里有兩個咖啡師,如果每個咖啡師都用相同的通知名,顧客便會收到毫無意義的通知,甚至更糟的是,會收到一杯含有大豆印度茶并且不含咖啡因的香草拿鐵而不是一杯拿鐵咖啡。

classBarista{

staticletcoffeeMadeNotification ="coffeeMadeNotification"

}

classTrainee:Barista{ }

...

NotificationCenter.default.

.postNotificationName(Trainee.coffeeMadeNotification)

使用字符串作為名稱的通知

我會避免使用字符串類型的通知,你也應該如此,因為這樣只會產出容易出錯的代碼。永遠不要相信人們避免拼寫錯誤或在沒有自動補全功能環境下編程的能力。

NSNotificationCenter.defaultCenter()

.postNotificationName("coffeeMadNotfication")

替代方案

更多的時候,我會盡可能使用代理模式來代替觀察者模式。代理模式與觀察者模式非常相似,但并不是一對多的關系,代理模式是一對一的關系。雖然代理模式也有自己的一些問題和限制,但它避免了我上面列出的問題,所以在我看來這種模式是更可靠的選擇。不過今天并不會深入探討這些問題。

通知協議

protocolNotifier{ }

我們可以設計一個協議來解決上面列出的所有問題,于是接下來挨個研究下這些問題,然后實現一個更 Swift 化的、有統一變化的NSNotificationCenter實現。

保證觀察對象的一致性

協議非常有用,因為想要遵守某個協議,就必須強制符合其規范。所以針對于這個協議,我們將給它設置一個關聯類型:

protocolNotifier{

associatedTypeNotification:RawRepresentable

}

從現在開始,如果在項目中的類或結構體想要發布通知,那就應該遵守Notifier協議,并提供遵守RawRepresentable協議的關聯類型。

classBarista:Notifier{

enumNotification:String{

casemakingCoffee

casecoffeeMade

}

}

在 Swift 中,由于枚舉也可以遵守RawRepresentable協議,所以可以使用一個String類型的枚舉,并命名相應的通知。

letcoffeeMade =Barista.Notification.coffeeMade.rawValue

NSNotificationCenter.defaultCenter()

.postNotificationName(coffeeMade)

避免通知名稱沖突

同樣,枚舉在這方面也起了很大作用,因為它可以讓我們避免重復定義。如果我們創建了多個makeCoffee的枚舉,編譯器將提示錯誤。然而,這并不能解決具有不同類或結構但具有相同枚舉名稱的問題。

letbaristaNotification =Barista.Notification.coffeeMade.rawValue

lettraineeNotification =Trainee.Notification.coffeeMade.rawValue

// baristaNotification: coffeeMade

// traineeNotification: coffeeMade

如上所見,需要為這些通知創建一個唯一的命名空間,來保證通知名稱之間沒有任何沖突。使用對應的對象名稱是一種很好的解決方案,因為編譯器不允許類或結構體具有相同的名稱。

letbaristaNotification =

"\(Barista).\(Barista.Notification.coffeeMade.rawValue)"

lettraineeNotification =

"\(Trainee).\(Trainee.Notification.coffeeMade.rawValue)"

// baristaNotification: Barista.coffeeMade

// traineeNotification: Trainee.coffeeMade

到目前為止都很順利,但是現在我們的實現方案到了一個左右為難的境地。一方面,我們解決了命名空間重復的問題,但另一方面我們的代碼看起來像是一坨垃圾。的確,雖然已經實現了一些統一性,但是如果沒有任何保護措施來防止我們自己和協作的開發人員忘記添加命名空間,那么這個方案是毫無意義的吧?

通知實現

對你來說幸運的是,我自己已經考慮到這一點,并避免了上述的糟糕情況。我們將進一步擴展我們的協議,并在 NSNotificationCenter 功能調用方面添加一些很友好的符合Swift API 指南的、特定類型的語法糖。

通知名稱

Barista.coffeeMade

我們通常希望使用自己的通知命名空間和名稱,因此會創建一個以通知枚舉為參數的函數,這個函數會在我們發出通知和移除觀察者時返回安全的通知名稱。這個函數也是私有的,因為我們并不希望外部的代碼訪問此功能,而是由自己和同事強制地遵守通知協議,從而具備了本來實現不了的優點。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容