别再面向 for 循环编程了,JDK 自带的观察者模式就很香!
大家好,你還在面向 for 循環(huán)編程嗎?
還有誰不會用觀察者模式嗎?
本篇帶來《觀察者模式》理論及實(shí)戰(zhàn)~
???
什么是觀察者模式?
觀察者模式(Observer Pattern)定義了對象間的一種一對多的依賴關(guān)系,這樣只要一個(gè)對象的狀態(tài)發(fā)生改變,其依賴的所有相關(guān)對象都會得到通知并自動(dòng)更新。
在觀察者模式中,發(fā)生改變的對象叫做觀察目標(biāo),而被通知更新的對象稱為觀察者,一個(gè)觀察目標(biāo)對應(yīng)多個(gè)觀察者,觀察者一般是一個(gè)列表集合,可以根據(jù)需要?jiǎng)討B(tài)增加和刪除,易于擴(kuò)展。
使用觀察者模式的優(yōu)點(diǎn)在于觀察目標(biāo)和觀察者之間是抽象松耦合關(guān)系,降低了兩者之間的耦合關(guān)系。
?
發(fā)布-訂閱模式
觀察者模式很多地方也叫發(fā)布-訂閱模式(Publish/Subscribe),其實(shí)也可以這么理解,不過兩者之間還是略有不同。
觀察者模式中的觀察者是直接綁定觀察目標(biāo),觀察目標(biāo)要維護(hù)一套觀察者列表,兩者是有一個(gè)基于接口的組合依賴關(guān)系的,所以說觀察者模式雖然是松耦合的,但并不是完全解耦的。
而發(fā)布-訂閱模式中的發(fā)布者和訂閱者兩者并沒有任何聯(lián)系,發(fā)布者通過中間方發(fā)布一個(gè)主題(Topic),訂閱者通過中間方(調(diào)度中心)訂閱一個(gè)主題(Topic),發(fā)布者狀態(tài)的變更也并不會直接通知訂閱者,而要通過中間方進(jìn)行通知,或者訂閱者自行從中間方拉取,所以說發(fā)布-訂閱模式是完全解耦的。
一圖搞懂它們的關(guān)系:
觀察者模式和訂閱發(fā)布模式的區(qū)別從圖片看兩者是有差別的,統(tǒng)一都叫觀察者模式,也沒毛病。
?
觀察者模式輪子
因觀察者模式應(yīng)用比較廣泛,所以 JDK 工具包從 1.0 版本里面自帶了觀察者模式模板套裝,我們根據(jù)其模板很方便就能實(shí)現(xiàn)觀察者模式,不需要再重復(fù)造輪子了。
觀察者目標(biāo)類:
java.util.Observable
里面兩個(gè)最重要的變量:
changed:觀察目標(biāo)狀態(tài)是否變更,默認(rèn)為:false;
obs:觀察者列表(observers),一個(gè)線程安全的列表集合:Vector,默認(rèn)為空集合;
里面的重要的方法都是和觀察目標(biāo)狀態(tài)和觀察者相關(guān)的,一看就清楚,這里就不介紹了。
觀察者接口:
java.util.Observable
public?interface?Observer?{/***?This?method?is?called?whenever?the?observed?object?is?changed.?An*?application?calls?an?<tt>Observable</tt>?object's*?<code>notifyObservers</code>?method?to?have?all?the?object's*?observers?notified?of?the?change.**?@param???o?????the?observable?object.*?@param???arg???an?argument?passed?to?the?<code>notifyObservers</code>*?????????????????method.*/void?update(Observable?o,?Object?arg); }觀察者接口只有一個(gè) update 方法,用來通知觀察者自己更新。
?
觀察者模式實(shí)戰(zhàn)
OK,知道了 JDK 自帶了這兩個(gè)東東,現(xiàn)在就來實(shí)現(xiàn)一個(gè)簡單的觀察者模式的應(yīng)用場景,模擬公眾號文章推送,觀察目標(biāo)是棧長我,觀察者是你們大家,我在公眾號棧推一篇文章,你們都能接收到更新通知并能閱讀。
新增觀察目標(biāo)類:
import?lombok.Getter;import?java.util.Observable;/***?觀察目標(biāo):棧長*/ @Getter public?class?JavaStackObservable?extends?Observable?{private?String?article;/***?發(fā)表文章*?@param?article*/public?void?publish(String?article){//?發(fā)表文章this.article?=?article;//?改變狀態(tài)this.setChanged();//?通知所有觀察者this.notifyObservers();}}觀察目標(biāo)的邏輯是先發(fā)表文章,再改變觀察目標(biāo)的狀態(tài),再通知所有觀察者。
我們來重點(diǎn)看 notifyObservers 方法的源碼:
先獲取同步鎖,判斷狀態(tài)是否更新,如已更新則清空觀察目標(biāo)狀態(tài),然后再使用 for 循環(huán)遍歷所有觀察者,一一調(diào)用觀察者的更新方法通知觀察者更新。
新增觀察者類:
import?lombok.NonNull; import?lombok.RequiredArgsConstructor;import?java.util.Observable; import?java.util.Observer;/***?觀察者:讀者粉絲*/ @RequiredArgsConstructor public?class?ReaderObserver?implements?Observer?{@NonNullprivate?String?name;private?String?article;@Overridepublic?void?update(Observable?o,?Object?arg)?{//?更新文章updateArticle(o);}private?void?updateArticle(Observable?o)?{JavaStackObservable?javaStackObservable?=?(JavaStackObservable)?o;this.article?=?javaStackObservable.getArticle();System.out.printf("我是讀者:%s,文章已更新為:%s\n",?this.name,?this.article);}}觀察者的邏輯是獲取到觀察者目標(biāo)實(shí)例對象,然后再用觀察目標(biāo)對象的文章信息更新為自己的文章信息,最后輸出某某某的文章已更新。
觀察者只要實(shí)現(xiàn) Observer 這個(gè)接口的 update 方法即可,用于觀察目標(biāo)進(jìn)行調(diào)用通知。
觀察目標(biāo)和觀察者類結(jié)構(gòu)圖如下:
新增測試類:
/***?觀察者:讀者粉絲*/ public?class?ObserverTest?{public?static?void?main(String[]?args)?{//?創(chuàng)建一個(gè)觀察目標(biāo)JavaStackObservable?javaStackObservable?=?new?JavaStackObservable();//?添加觀察者javaStackObservable.addObserver(new?ReaderObserver("小明"));javaStackObservable.addObserver(new?ReaderObserver("小張"));javaStackObservable.addObserver(new?ReaderObserver("小愛"));//?發(fā)表文章javaStackObservable.publish("什么是觀察者模式?");}}觀察目標(biāo)、觀察者的創(chuàng)建并沒有先后順序要求,重點(diǎn)是發(fā)表文章通知觀察者之前,觀察目標(biāo)要添加觀察者列表這一步不能少。
輸出結(jié)果:
通過這個(gè)簡單的文章推送實(shí)踐,大家應(yīng)該對觀察者模式有一個(gè)基本的認(rèn)知了,在實(shí)際工作當(dāng)中也可以有很多場景拿去用,就一對多的依賴關(guān)系都可以考慮使用觀察者模式。
?
總結(jié)
不容易啊,陸陸續(xù)續(xù)又肝了大半天,你學(xué)會觀察者模式了嗎?
觀察者模式的優(yōu)點(diǎn)是為了給觀察目標(biāo)和觀察者解耦,而缺點(diǎn)也很明顯,從上面的例子也可以看出,如果觀察者對象太多的話,有可能會造成內(nèi)存泄露。
另外,從性能上面考慮,所有觀察者的更新都是在一個(gè)循環(huán)中排隊(duì)進(jìn)行的,所以觀察者的更新操作可以考慮做成線程異步(或者可以使用線程池)的方式,以提升整體效率。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的别再面向 for 循环编程了,JDK 自带的观察者模式就很香!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微信小程序~自定义属性设置和获取(dat
- 下一篇: 序列化组件