觀察者模式還是非常常用的,常見于基于zookeeper進行分布式系統(tǒng)之間的協(xié)調(diào)工作,比如分布式鎖的注冊以及監(jiān)聽是否釋放。還有就是兩個系統(tǒng)之間如果做了異步的處理,那么如果A系統(tǒng)發(fā)送異步請求給了B系統(tǒng),但是要得到B系統(tǒng)的一個狀態(tài)改變的消息,可以采用觀察者模式。
創(chuàng)新互聯(lián)建站主營長安網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app軟件開發(fā),長安h5小程序定制開發(fā)搭建,長安網(wǎng)站營銷推廣歡迎長安等地區(qū)企業(yè)咨詢
使用場景
在Zookeeper中的監(jiān)聽回調(diào)機制,以及分布式鎖,都是使用了觀察者模式。
基于zookeeper去做分布式鎖
(1)系統(tǒng)A嘗試獲取zookeeper上的一個鎖,獲取到了
(2)系統(tǒng)B嘗試獲取zookeeper上的一個鎖,被系統(tǒng)A給鎖了,沒有獲取到鎖,此時系統(tǒng)B在zookeeper上可以注冊一個監(jiān)聽器(觀察者)
(3)系統(tǒng)A一旦將鎖給釋放了,zookeeper感受到鎖被釋放了,就會立即通知系統(tǒng)B注冊的那個監(jiān)聽器
(4)系統(tǒng)B就立即被通知到了,系統(tǒng)A釋放了鎖,系統(tǒng)B可以重新嘗試在zookeeper上加鎖
電商系統(tǒng)里,也有這種場景,如果兩個系統(tǒng)之間走了異步請求,那么可以基于上面那種觀察者模式現(xiàn)在一個進程內(nèi)實現(xiàn)監(jiān)聽,以后拆分微服務(wù)分布式架構(gòu)了,可以改成基于zookeeper來做分布式協(xié)調(diào)。
系統(tǒng)A發(fā)送了一條消息到內(nèi)存隊列,系統(tǒng)B獲取了消息開始執(zhí)行操作
但是系統(tǒng)A需要知道系統(tǒng)B的一個執(zhí)行的結(jié)果如何,
此時系統(tǒng)A需要注冊一個觀察者到系統(tǒng)B上去,系統(tǒng)B執(zhí)行完了之后,將執(zhí)行的結(jié)果,反過來通知給系統(tǒng)
我們就可以基于觀察者模式去做。
官方解釋:
觀察者模式(有時又被稱為發(fā)布-訂閱Subscribe模式、模型-視圖View模式、源-收聽者Listener模式或從屬者模式)是軟件設(shè)計模式的一種。在此種模式中,一個目標(biāo)物件管理所有相依于它的觀察者物件,并且在它本身的狀態(tài)改變時主動發(fā)出通知。這通常透過呼叫各觀察者所提供的方法來實現(xiàn)。此種模式通常被用來實作事件處理系統(tǒng)。
個人理解:
觀察者模式是一種思想,不需要人為的去關(guān)注觀察者和被觀察者之間是怎樣聯(lián)系的,實現(xiàn)了解耦,只需要對象去注冊被觀察者(Observerable)與觀察者(Observer),然后被觀察者去添加一個或者多個觀察者,當(dāng)被觀察者發(fā)生變動就會立即通知所有的觀察者,下面讓我們來看看是怎樣實現(xiàn)這個功能的。
被觀察者首先通過addObserver(Observer o)來添加一個觀察者,底層代碼中會把這個對象o放進一個vector集合中,當(dāng)然也可以添加多個觀察者,當(dāng)觀察者發(fā)生變動的時候就會觸發(fā)
setChanged();
notifyObservers();
這兩個方法,然后底層代碼中就回去遍歷裝有觀察者的那個vector,然后
for (int i = arrLocal.length-1; i=0; i--)
((Observer)arrLocal[i]).update(this, arg);
調(diào)用update方法通知每一個觀察者,這樣觀察者對象中就可以拿到被觀察者的相關(guān)對象和信息
當(dāng)我們想訂一份報紙,我們先去郵局找到報紙的編號后填寫訂閱單并繳費。當(dāng)報社有新報紙發(fā)出時,郵局會將我們訂閱的報紙發(fā)給我們。
為了簡單我們?nèi)サ羿]局環(huán)節(jié)簡化成:報社有新報紙后馬上通知用戶,這就是觀察者。
定義對象間的一對多關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生變化時,所依賴于它的對象都得到通知并主動更新。 在觀察者模式中,多個訂閱者成為觀察者(Observer),被觀察的對象成為目標(biāo)(Subject)。觀察者的UML模型如下:
先定義Subject并寫一個ConcreteSubject繼承Subject:
再定義一個接口Observer,并寫一個ConcreteObserver實現(xiàn)Observer接口:
最后看看主函數(shù)方法:
打印出來的結(jié)果:
在實現(xiàn)觀察者模式的時候,一定要注意觸發(fā)通知的時機。一般情況下是在完成了狀態(tài)改變之后觸發(fā),因為通知會傳遞數(shù)據(jù),比如在 setSubjectState 時先通知觀測者就會發(fā)生 錯誤 。
在觀察者模式的實現(xiàn)上,有推模式和拉模式兩種方式:
當(dāng)前上面的實現(xiàn)使用的就是拉模型。通過 (ConcreteSubject)subject 得到具體對象,獲得信息。
當(dāng)然Java本身就有觀察者模式的部分實現(xiàn),分別是 java.util.Observable java.util.Observable 。
下面看一個使用Java自帶觀察者模式的例子:
新的目標(biāo)直接繼承Java中定義的Observerable:
新的觀察者也直接實現(xiàn)Observer接口:
主函數(shù)和前面的相似:
打印出結(jié)果:
使用Java自帶的觀察者模式需要注意以下幾個問題:
觀察者(Observer)模式又名發(fā)布-訂閱(Publish/Subscribe)模式。GOF給觀察者模式如下定義:定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。
在這里先講一下面向?qū)ο笤O(shè)計的一個重要原則——單一職責(zé)原則。因此系統(tǒng)的每個對象應(yīng)該將重點放在問題域中的離散抽象上。因此理想的情況下,一個對象只做一件事情。這樣在開發(fā)中也就帶來了諸多的好處:提供了重用性和維護性,也是進行重構(gòu)的良好的基礎(chǔ)。
因此幾乎所有的設(shè)計模式都是基于這個基本的設(shè)計原則來的。觀察者模式的起源我覺得應(yīng)該是在GUI和業(yè)務(wù)數(shù)據(jù)的處理上,因為現(xiàn)在絕大多數(shù)講解觀察者模式的例子都是這一題材。但是觀察者模式的應(yīng)用決不僅限于此一方面。
下面我們就來看看觀察者模式的組成部分。
1)抽象目標(biāo)角色(Subject):目標(biāo)角色知道它的觀察者,可以有任意多個觀察者觀察同一個目標(biāo)。并且提供注冊和刪除觀察者對象的接口。目標(biāo)角色往往由抽象類或者接口來實現(xiàn)。
2)抽象觀察者角色(Observer):為那些在目標(biāo)發(fā)生改變時需要獲得通知的對象定義一個更新接口。抽象觀察者角色主要由抽象類或者接口來實現(xiàn)。
3)具體目標(biāo)角色(ConcreteSubject):將有關(guān)狀態(tài)存入各個ConcreteObserver對象。當(dāng)它的狀態(tài)發(fā)生改變時,向它的各個觀察者發(fā)出通知。
4)具體觀察者角色(ConcreteObserver):存儲有關(guān)狀態(tài),這些狀態(tài)應(yīng)與目標(biāo)的狀態(tài)保持一致。實現(xiàn)Observer的更新接口以使自身狀態(tài)與目標(biāo)的狀態(tài)保持一致。在本角色內(nèi)也可以維護一個指向ConcreteSubject對象的引用。
定義 :對象間的一種一對多的依賴關(guān)系,使得當(dāng)每一個對象改變狀態(tài),則所有依賴于他的對象都會得到通知,并自動更新。
交互對象之間松耦合
1)觀察者定義了對象之間一對多的關(guān)系
2)被觀察者用一個共同的接口來更新觀察者
3)觀察者和被觀察者用松耦合方式結(jié)合,被觀察者不知道觀察者的細節(jié),只知道觀察者實現(xiàn)了觀察者接口
優(yōu)點:
1)觀察者與被觀察者抽象耦合,容易擴展;
2)建立了一套觸發(fā)機制。
缺點:
1)循環(huán)依賴會導(dǎo)致系統(tǒng)崩潰;
2)觀察者太多會浪費時間。
1)定義對象之間的一對多依賴關(guān)系而不使對象緊密耦合。
2)確保當(dāng)一個對象改變狀態(tài)時,自動更新開放數(shù)量的從屬對象。
3)一個對象應(yīng)該可以通知開放式數(shù)量的其他對象。
觀察者模式 vs 發(fā)布-訂閱模式
觀察者模式(Observer)
如何使用 Java8 實現(xiàn)觀察者模式?(上)
如何使用 Java8 實現(xiàn)觀察者模式?(下)
spring 事件為bean 與 bean之間傳遞消息。一個bean處理完了希望其余一個接著處理.這時我們就需要其余的一個bean監(jiān)聽當(dāng)前bean所發(fā)送的事件.
spring事件使用步驟如下:
1.先自定義事件:你的事件需要繼承 ApplicationEvent
2.定義事件監(jiān)聽器: 需要實現(xiàn) ApplicationListener
3.使用容器對事件進行發(fā)布
最后有一個思考 :ApplicationEvent事件執(zhí)行部分和起一個TaskExecutor去執(zhí)行 有啥區(qū)別嗎?反正都是異步。
可以這樣實現(xiàn);
還可以這樣實現(xiàn);
我的思考:ApplicationEvent是觀察者設(shè)計模式,這種設(shè)計模式使得主題和觀察者之間的耦合度降低,松耦合是面向?qū)ο笤O(shè)計中很重要的一個原則,最終也是使用@Async來實現(xiàn)異步。而TaskExecutor則是啟動一個線程池任務(wù)異步執(zhí)行任務(wù),兩者效果一樣,但原理不同。
通過我的思考,又帶來一個疑問:那觀察者模式是不是就是我們MQ中的發(fā)布訂閱模式呢?只不過觀察者模式是進程內(nèi)的,而MQ是跨進程的?就這唯一的區(qū)別嗎?
經(jīng)過一些資料的查閱:大多數(shù)地方觀察者模式 約等于 發(fā)布訂閱模式,但是觀察者模式是由具體目標(biāo)調(diào)度的,而發(fā)布/訂閱模式是統(tǒng)一由調(diào)度中心調(diào)的,所以觀察者模式的訂閱者與發(fā)布者之間是存在依賴的,而發(fā)布/訂閱模式則不會。
所以說觀察者模式是小米加步槍,發(fā)布訂閱模式是95式自動步槍,是它的進化版!