自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用脫口秀大會(huì)來(lái)講觀察者模式

開(kāi)發(fā) 前端
最近正在熱播的脫口秀大會(huì),想必大家都看過(guò)了吧,那這次我來(lái)帶著大家來(lái)看下大會(huì)上的觀察者模式吧。

[[426704]]

大家好,我是悟空。

最近正在熱播的脫口秀大會(huì),想必大家都看過(guò)了吧,那這次我來(lái)帶著大家來(lái)看下大會(huì)上的觀察者模式吧。

一、脫口秀

首先是脫口秀的角色劃分:

我們把脫口秀演員:當(dāng)做一個(gè)被被觀察者(Observable)。

4 位領(lǐng)笑員 + 180 位觀眾,當(dāng)做觀察者(Observer)。

領(lǐng)笑員的職責(zé):當(dāng)脫口秀演員表現(xiàn)好時(shí),拍燈,表示非常好笑。

觀眾的職責(zé):當(dāng)脫口秀演員表現(xiàn)好時(shí),拿起手中的遙控器,按下按鍵表示非常喜歡。

這種場(chǎng)景就非常符合觀察者模式了,簡(jiǎn)單來(lái)說(shuō)就是一批觀察者對(duì)要觀察的對(duì)象進(jìn)行觀察,對(duì)觀察對(duì)象進(jìn)行反應(yīng)。

說(shuō)完上面的例子,想必大家對(duì)觀察者模式已經(jīng)有了初步的印象了。

那我們?cè)賮?lái)看看在程序設(shè)計(jì)的世界中,觀察者模式是怎么樣的。

二、觀察者模式

GoF 設(shè)計(jì)模式那本書(shū)中講到:在對(duì)象之間定義一個(gè)一對(duì)多的依賴,當(dāng)一個(gè)對(duì)象狀態(tài)改變的時(shí)候,所有依賴的對(duì)象都會(huì)自動(dòng)收到通知,這就是觀察者模式。

觀察者模式有很多其他稱呼,比如發(fā)布訂閱,監(jiān)聽(tīng)回調(diào)等等,其實(shí)只要場(chǎng)景符合上面的描述,都可以叫做觀察者模式。

Java API 內(nèi)置了觀察者模式,非常方便使用。用法:java.util 包內(nèi)包含最基本的 Observer 接口(觀察者接口)和 Observable 類(被觀察者父類)。另外他們之間可以用推(push)或拉(pull)的方式傳送數(shù)據(jù)。

另外很重要的一點(diǎn):被觀察者和觀察者之間的關(guān)系是一對(duì)多的。如上面的脫口秀的例子,觀眾是很多個(gè),演員一次只有一個(gè)(或一個(gè)脫口秀組合)。

三、被觀察者怎么工作的?

只需要這個(gè)類繼承 Observable 類即可。我來(lái)帶著大家看下這個(gè) Observable 類的構(gòu)成。

添加觀察者

我們首先想一下,我們想要觀察別人的時(shí)候,是不是就需要被添加成別人的觀察者,那么就需要一個(gè)添加觀察者的方法,Observale 給我們提供了一個(gè)添加成為別人的觀察者的方法:addObserver。

存放觀察者

當(dāng)有很多想要成為觀察者的時(shí)候,是不是就得有個(gè)地方專門(mén)來(lái)存這些觀察者?

Observable 給我們提供了一個(gè)存放所有觀察者的地方:一個(gè) Vector 集合。

移除觀察者

當(dāng)我們不想被某個(gè)人觀察,是不是就移除掉就可以了。

Observable 給我們提供了一個(gè)移除觀察者的方法:deleteObserver。

被觀察者如何發(fā)出通知?

當(dāng)被觀察對(duì)象,想告訴觀察者,他的狀態(tài)已經(jīng)變了,是不是就要發(fā)個(gè)通知?

Observable 給我們提供了兩個(gè)方法:

notifyObservers() 或 notifyObservers(Object arg)。

區(qū)別就是一個(gè)帶參,一個(gè)不帶參。不帶參的方式常用在觀察者通過(guò) pull 的方式來(lái)獲取數(shù)據(jù)。

如下圖所示,通過(guò) push 的方式通知觀察者。

那么通知的具體細(xì)節(jié)是怎么樣的?

說(shuō)白了,就三步:

  • 被觀察對(duì)象,先判斷自己狀態(tài)是否有改變。
  • 從 vector 集合中獲取所有添加的觀察者。
  • 循環(huán)遍歷觀察者,調(diào)用觀察者的 update 方法。

看下源碼更清晰,注釋都加上了。

  1. public void notifyObservers(Object var1) { 
  2.  Object[] var2; 
  3.  synchronized(this) { 
  4.         //當(dāng)調(diào)用 setChange() 方法后,this.changed = true 
  5.         if (!this.changed) { 
  6.                 return
  7.         } 
  8.         // 獲取所有觀察者 
  9.         var2 = this.obs.toArray(); 
  10.         // 重置 change 狀態(tài) 
  11.            this.clearChanged(); 
  12.     } 
  13.     // 循環(huán)遍歷通知觀察者 
  14.     for(int var3 = var2.length - 1; var3 >= 0; --var3) { 
  15.         ((Observer)var2[var3]).update(this, var1); 
  16.     } 

為什么要有 setChanged?

在被觀察者發(fā)送通知前,被觀察對(duì)象都會(huì)調(diào)用下 setChanged() 方法,標(biāo)記狀態(tài)已經(jīng)改變了。

  1. protected synchronized void clearChanged() { 
  2.   this.changed = false

那為什么需要調(diào)用下這個(gè)?不調(diào)用可以嗎?

當(dāng)被觀察對(duì)象調(diào)用 notifyObservers 方法中,會(huì)判斷狀態(tài)是否有改變,如果沒(méi)有改變,則不會(huì)通知觀察者。

這樣做的好處:可以在通知觀察者時(shí)有更多的彈性。如果不想持續(xù)不斷地通知觀察者,就可以適當(dāng)?shù)乜刂?setChanged 方法的調(diào)用。

其他:還可以用 clearChanged,重置 changed 狀態(tài),hasChanged 方法獲取 changed 狀態(tài)。

四、觀察者如何工作的?

其實(shí)很簡(jiǎn)單,觀察者實(shí)現(xiàn)了 Observer 接口就可以成為觀察者。

  1. public interface Observer { 
  2.     void update(Observable var1, Object var2); 

然后觀察者實(shí)現(xiàn)了 update 方法,就是給被觀察對(duì)象來(lái)調(diào)用的。

關(guān)于推模式和拉模式的小插曲:

如果想用推模式,調(diào)用帶參的 notifyObservers 方法把參數(shù)傳給觀察者就可以了,如果想用拉模式,就需要主動(dòng)調(diào)用被觀察者的 get 數(shù)據(jù)的方法,用帶參的或不帶參的方式通知觀察者都是可以的。

五、代碼實(shí)現(xiàn)

我們把領(lǐng)笑員定義為 Leader 類,觀眾定義成 Viewer 類,脫口秀演員定義為 Actor 類。

領(lǐng)笑員都在看演員表演脫口秀,需要成為演員的觀察者。調(diào)用 actor.addObserver(leader) 就可以了.

觀眾也是類似,調(diào)用 actor.addObserver(viewer) 就好了。

根據(jù)前面講解的原理,領(lǐng)笑員和觀眾必須繼承 observer 接口,然后實(shí)現(xiàn) update 方法。

如下所示:當(dāng)收到通知后,做出相應(yīng)反應(yīng),比如拍燈。

演員的每次的梗說(shuō)完后,都會(huì)調(diào)用 setChanged() 方法,和 notifyObservers(參數(shù)) 來(lái)通知觀察者,然后所有觀察者的 update 方法都會(huì)被觸發(fā)。

來(lái)看下演員通知的代碼:

執(zhí)行結(jié)果如下,王勉的表現(xiàn)非常精彩,領(lǐng)笑員拍燈了!

源碼下載,在公眾號(hào)后臺(tái)回復(fù):觀察者。

好了,觀察者模式還是挺有意思的。那在電商中如何應(yīng)用的呢?

六、關(guān)于設(shè)計(jì)模式

上面關(guān)于觀察者和被觀察者的工作原理有些坑,不知道大家注意到?jīng)]?

  • 觀察者需要被添加到具體某個(gè)被觀察者的集合中,才能觀察,相當(dāng)于面向細(xì)節(jié)了,違背了面向抽象的原則。
  • Observable 是一個(gè)類,而不是一個(gè)接口,而且 Observable 也沒(méi)有實(shí)現(xiàn)接口,這個(gè)就違背了面向接口編程。
  • 必須有一個(gè)類來(lái)繼承 Observable ,如果某個(gè)類相同時(shí)擁有 Observer 類的功能,又想擁有另外一個(gè)類的功能,那么就會(huì)陷入兩難,因?yàn)?Java 不支持多重繼承,限制了 Observable 的復(fù)用潛力。
  • 另外 Observer API 中的 setChanged() 方法被保護(hù)起來(lái)了(被定義成 protected 方法),那么除非繼承 Observable,否則無(wú)法創(chuàng)建 Observable 實(shí)例并組合到你自己的對(duì)象中。違反了“多用組合,少用繼承”的原則。

七、架構(gòu)設(shè)計(jì)的問(wèn)題

問(wèn)題1:上面的觀察者模式都是同步阻塞的方式,被觀察者需要等待觀察者全部執(zhí)行完后,才會(huì)執(zhí)行后續(xù)代碼。怎么通過(guò)異步的方式來(lái)通知觀察者呢?

  • 方案1:?jiǎn)?dòng)一個(gè)線程來(lái)調(diào)用 notifyObservers 方法。
  • 方案2:Google Guava EventBus 框架的設(shè)計(jì)思想

問(wèn)題2:跨進(jìn)程怎么通信?

  • 方案1:我們看到被觀察者每次都要調(diào)用觀察者的 update 方法來(lái)通知觀察者,所以跨進(jìn)程該怎么做?我們可以同步調(diào)用 RPC 接口來(lái)實(shí)現(xiàn)。
  • 方案2:消息隊(duì)列,可以有多個(gè)消費(fèi)者和生產(chǎn)者,消費(fèi)者訂閱消息,類似觀察者。但是引入了消息隊(duì)列,增加了維護(hù)成本。

問(wèn)題3:跨機(jī)器怎么通信?

  • 還是引入消息隊(duì)列。

八、電商中應(yīng)用

商品庫(kù)存可以作為一個(gè)被觀察者,商品入庫(kù)單作為觀察者,當(dāng)商品庫(kù)存變了后,需要生成一個(gè)商品入庫(kù)單,就可以用觀察者模式,商品入庫(kù)單和商品庫(kù)存進(jìn)行解耦,如果后續(xù)還要生成其他類型的入庫(kù)單再加上發(fā)送一條消息給管理員,直接添加觀察者就可以了。

九、后記

本篇通過(guò)脫口秀大會(huì)來(lái)講解觀察者模式,涉及到了三種角色,領(lǐng)笑員,觀眾,脫口秀演員。

然后詳細(xì)講解了觀察者和被觀察者的工作原理,另外探討了這種模式有哪些設(shè)計(jì)模式相關(guān)的問(wèn)題。

然后從架構(gòu)設(shè)計(jì)的角度來(lái)分析了觀察者模式引入的問(wèn)題:同步調(diào)用,跨進(jìn)程通信,跨機(jī)器通信。

最后簡(jiǎn)單講了下電商中的應(yīng)用場(chǎng)景,拋轉(zhuǎn)引玉,希望大家留言探討。

本文轉(zhuǎn)載自微信公眾號(hào)「悟空聊架構(gòu)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系悟空聊架構(gòu)公眾號(hào)。

責(zé)任編輯:武曉燕 來(lái)源: 悟空聊架構(gòu)
相關(guān)推薦

2015-08-18 00:01:00

青云

2020-10-26 08:45:39

觀察者模式

2013-11-26 17:09:57

Android設(shè)計(jì)模式

2021-07-08 11:28:43

觀察者模式設(shè)計(jì)

2021-09-06 10:04:47

觀察者模式應(yīng)用

2022-01-29 22:12:35

前端模式觀察者

2011-04-29 09:22:22

2012-08-27 10:52:20

.NET架構(gòu)觀察者模式

2021-03-29 07:14:28

Spring觀察者模式

2024-12-03 09:34:35

觀察者模 式編程Javav

2015-11-25 11:10:45

Javascript設(shè)計(jì)觀察

2024-02-18 12:36:09

2018-04-04 15:41:09

白熊視頻CTO說(shuō)薪資倒掛

2024-06-04 13:11:52

Python行為設(shè)計(jì)模式開(kāi)發(fā)

2009-03-30 09:39:04

觀察者思想換位設(shè)計(jì)模式

2015-11-16 15:25:32

UPYUN

2021-01-25 05:38:04

設(shè)計(jì)原理VueSubject

2020-09-27 06:58:09

羅永浩熱搜
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)