觀察者模式(Observer Pattern),也叫作發佈訂閱模式(Publish-Subscribe),它定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。java
水果店進口水果銷量良好,特別是進口蛇果,目前還沒到貨,有幾個女孩小美、小靜、小陶想預約進口蛇果,在到貨以前就給水果老闆說過,等到貨後電話或者微信第一時間通知她們。
數組
類圖以下
安全
Observer
,當收到被觀察者通知後,執行update()
方法;FruitObservable
,持有一個類型爲Vector<Observer>
的屬性,存放觀察者集合,同時定義三個方法addObserver()
,deleteObserver()
,notifyObservers()
,分別爲添加預約顧客,刪除預約顧客,通知已預訂顧客;FruitObservable
抽象接口的實現類爲進口蛇果ImportedRedDeliciousApple
;Observer
接口的實現類爲預約進口蛇果的客戶類CustomerObserver
;代碼實現以下微信
package com.wzj.observer.example1; /** * @Author: wzj * @Date: 2019/10/3 14:53 * @Desc: 觀察者接口類 */ public interface Observer { //收到通知後觀察者更新 void update(); }
package com.wzj.observer.example1; import java.util.Vector; /** * @Author: wzj * @Date: 2019/10/3 14:50 * @Desc: 水果店被觀察者類 */ public abstract class FruitObservable { //線程安全的容器類 protected Vector<Observer> obs = new Vector<>(); //添加預約顧客 public void addObserver(Observer o) { obs.addElement(o); } //刪除固定顧客 public void deleteObserver(Observer o) { obs.removeElement(o); } //通知已預約顧客 public abstract void notifyObservers(); }
package com.wzj.observer.example1; /** * @Author: wzj * @Date: 2019/10/3 15:19 * @Desc: 進口蛇果類 */ public class ImportedRedDeliciousApple extends FruitObservable { @Override public void notifyObservers() { System.out.println("通知: 進口蛇果已到..."); for(Observer o : obs) { //通知每位預約顧客 o.update(); } } }
package com.wzj.observer.example1; /** * @Author: wzj * @Date: 2019/10/3 15:25 * @Desc: 預約進口蛇果的客戶類 */ public class CustomerObserver implements Observer { //觀察者姓名 private String name; public CustomerObserver(String name){ this.name = name; } @Override public void update() { System.out.println(name + "已收到通知, 將立馬過來購買!"); } }
客戶端測試類多線程
package com.wzj.observer.example1; /** * @Author: wzj * @Date: 2019/10/3 15:38 * @Desc: */ public class Client { public static void main(String[] args) { //進口蛇果 ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple(); //初始化預約客戶類 CustomerObserver mei = new CustomerObserver("小美"); CustomerObserver jing = new CustomerObserver("小靜"); CustomerObserver tao = new CustomerObserver("小陶"); //將小美、小靜、小陶加入水果店的預約客戶列表中 apple.addObserver(mei); apple.addObserver(jing); apple.addObserver(tao); //通知客戶 apple.notifyObservers(); } }
執行結果以下app
通知: 進口蛇果已到... 小美已收到通知, 將立馬過來購買! 小靜已收到通知, 將立馬過來購買! 小陶已收到通知, 將立馬過來購買!
類圖以下
異步
Observer
,當收到被觀察者通知後,執行update()
方法;Observable
,boolean
類型的屬性changed
表示狀態是否變化,持有一個類型爲Vector<Observer>
的屬性obs
;Observable
抽象接口的實現類爲進口蛇果ImportedRedDeliciousApple
;Observer
接口的實現類爲預約進口蛇果的客戶類CustomerObserver
;代碼實現以下ide
package com.wzj.observer.example2; import java.util.Observable; import java.util.Observer; /** * @Author: wzj * @Date: 2019/10/3 17:09 * @Desc: 預約客戶類,繼承觀察者接口 */ public class CustomerObserver implements Observer { private String name; public CustomerObserver(String name){ this.name = name; } @Override public void update(Observable o, Object arg) { System.out.println(name + "已收到通知, 將立馬過來購買!"); } }
package com.wzj.observer.example2; import java.util.Observable; import java.util.Observer; /** * @Author: wzj * @Date: 2019/10/3 17:02 * @Desc: 進口蛇果類,繼承jdk的Observable接口 */ public class ImportedRedDeliciousApple extends Observable { public void peform() { System.out.println("通知: 進口蛇果已到..."); //設置狀態改變 this.setChanged(); this.notifyObservers(); } }
客戶端代碼以下源碼分析
package com.wzj.observer.example2; /** * @Author: wzj * @Date: 2019/10/3 15:38 * @Desc: 使用jdk自帶的Observable類和Observer接口 */ public class Client { public static void main(String[] args) { //進口蛇果 ImportedRedDeliciousApple apple = new ImportedRedDeliciousApple(); //初始化預約客戶類 CustomerObserver mei = new CustomerObserver("小美"); CustomerObserver jing = new CustomerObserver("小靜"); CustomerObserver tao = new CustomerObserver("小陶"); //將小美、小靜、小陶加入水果店的預約客戶列表中 apple.addObserver(mei); apple.addObserver(jing); apple.addObserver(tao); //通知客戶 apple.peform(); } }
執行結果測試
通知: 進口蛇果已到... 小陶已收到通知, 將立馬過來購買! 小靜已收到通知, 將立馬過來購買! 小美已收到通知, 將立馬過來購買!
源碼分析
/** 被觀察者 */ public class Observable { private boolean changed = false; private Vector<Observer> obs; /** 構造方法 */ public Observable() { obs = new Vector<>(); } /** 添加觀察者,並加同步控制保證多線程安全 */ public synchronized void addObserver(Observer o) { if (o == null) throw new NullPointerException(); if (!obs.contains(o)) { obs.addElement(o); } } /** 刪除觀察者,並加同步控制保證多線程安全 */ public synchronized void deleteObserver(Observer o) { obs.removeElement(o); } /** 通知觀察者 */ public void notifyObservers() { //通知全部觀察者 notifyObservers(null); } public void notifyObservers(Object arg) { Object[] arrLocal; synchronized (this) { //狀態未改變時返回 if (!changed) return; //狀態改變後,將集合轉爲數組 arrLocal = obs.toArray(); //狀態恢復初始值 clearChanged(); } //遍歷數組,逐個通知觀察者 for (int i = arrLocal.length-1; i>=0; i--) ((Observer)arrLocal[i]).update(this, arg); } /** 狀態改變 */ protected synchronized void setChanged() { changed = true; }
優勢
觀察者和被觀察者之間是抽象耦合。
無論是增長觀察者仍是被觀察者都很是容易擴展,並且在Java中都已經實現的抽象層級的定義,因此儘可能使用JDK自帶的接口。
與責任鏈結合使用
在不少系統中,Observer模式每每和責任鏈共同負責對於事件的處理,其中的某一個observer負責是否將事件進一步傳遞。
缺點
觀察者模式須要考慮一下開發效率和運行效率問題,一個被觀察者,多個觀察者,開發和調試就會比較複雜,並且在Java中消息的通知默認是順序執行,一個觀察者卡殼,會影響總體的執行效率。在這種狀況下,通常考慮採用異步的方式。