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