「補課」進行時:設計模式系列java
觀察者模式(Observer Pattern)也叫作發佈訂閱模式(Publish/subscribe),它是一個在項目中常常使用的模式,其定義以下:設計模式
Define a one-to-many dependency between objects so that when oneobject changes state,all its dependents are notified and updatedautomatically.(定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則全部依賴於它的對象都會獲得通知並被自動更新。)數組
Subject 被觀察者:微信
public abstract class Subject { // 定義一個觀察者數組 private Vector<Observer> obsVector = new Vector<>(); // 添加一個觀察者 public void addObserver(Observer obsVector) { this.obsVector.add(obsVector); } // 刪除一個觀察者 public void delObserver(Observer observer) { this.obsVector.remove(observer); } // 通知全部觀察者 public void notifyObservers() { for (Observer obs : this.obsVector) { obs.update(); } } }
ConcreteSubject 具體的被觀察者:併發
public class ConcreteSubject extends Subject { public void doSomething() { // 具體的業務 super.notifyObservers(); } }
Observer 觀察者:ide
public interface Observer { void update(); }
ConcreteObserver 具體的觀察者:測試
public class ConcreteObserver implements Observer{ @Override public void update() { System.out.println("進行消息處理"); } }
測試場景類:this
public class Test { public static void main(String[] args) { // 建立一個被觀察者 ConcreteSubject subject = new ConcreteSubject(); // 建立一個觀察者 Observer observer = new ConcreteObserver(); // 觀察者觀察被觀察者 subject.addObserver(observer); // 觀察者開始活動了 subject.doSomething(); } }
觀察者模式是設計模式中的超級模式,有關他的應用隨處可見。設計
就好比說微信公衆號,我天天推送一篇博文內容,訂閱的用戶都可以在我發佈推送以後及時接收到推送,方便地在手機端進行閱讀。3d
訂閱者接口(觀察者)
public interface Subscriber { void receive(String publisher, String articleName); }
微信客戶端(具體觀察者)
public class WeChatClient implements Subscriber { private String username; public WeChatClient(String username) { this.username = username; } @Override public void receive(String publisher, String articleName) { System.out.println(String.format("用戶<%s> 接收到 <%s>微信公衆號 的推送,文章標題爲 <%s>", username, publisher, articleName)); } }
一個微信客戶端(具體觀察者)
public class Publisher { private List<Subscriber> subscribers; private boolean pubStatus = false; public Publisher() { subscribers = new ArrayList<>(); } protected void subscribe(Subscriber subscriber) { this.subscribers.add(subscriber); } protected void unsubscribe(Subscriber subscriber) { if (this.subscribers.contains(subscriber)) { this.subscribers.remove(subscriber); } } protected void notifySubscribers(String publisher, String articleName) { if (this.pubStatus == false) { return; } for (Subscriber subscriber : this.subscribers) { subscriber.receive(publisher, articleName); } this.clearPubStatus(); } protected void setPubStatus() { this.pubStatus = true; } protected void clearPubStatus() { this.pubStatus = false; } }
具體目標
public class WeChatAccounts extends Publisher { private String name; public WeChatAccounts(String name) { this.name = name; } public void publishArticles(String articleName, String content) { System.out.println(String.format("\n<%s>微信公衆號 發佈了一篇推送,文章名稱爲 <%s>,內容爲 <%s> ", this.name, articleName, content)); setPubStatus(); notifySubscribers(this.name, articleName); } }
測試類
public class Test { public static void main(String[] args) { WeChatAccounts accounts = new WeChatAccounts("極客挖掘機"); WeChatClient user1 = new WeChatClient("張三"); WeChatClient user2 = new WeChatClient("李四"); WeChatClient user3 = new WeChatClient("王五"); accounts.subscribe(user1); accounts.subscribe(user2); accounts.subscribe(user3); accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容..."); accounts.unsubscribe(user1); accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容...."); } }
測試結果
<極客挖掘機>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 觀察者模式及典型應用>,內容爲 <觀察者模式的內容...> 用戶<張三> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 用戶<李四> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 用戶<王五> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> <極客挖掘機>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 單例模式及典型應用>,內容爲 <單例模式的內容....> 用戶<李四> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用> 用戶<王五> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用>
觀察者模式在 Java 語言中的地位很是重要。在 JDK 的 java.util
包中,提供了 Observable
類以及 Observer
接口,它們構成了JDK對觀察者模式的支持。
在 java.util.Observer
接口中,僅有一個 update(Observable o, Object arg)
方法,當觀察目標發生變化時被調用:
public interface Observer { void update(Observable o, Object arg); }
java.util.Observable
類則爲目標類:
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); } // 通知方法,用於在方法內部循環調用向量中每個觀察者的update()方法 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); } // 用於清空向量,即刪除向量中全部觀察者對象 public synchronized void deleteObservers() { obs.removeAllElements(); } // 該方法被調用後會設置一個boolean類型的內部標記變量changed的值爲true,表示觀察目標對象的狀態發生了變化 protected synchronized void setChanged() { changed = true; } // 用於將changed變量的值設爲false,表示對象狀態再也不發生改變或者已經通知了全部的觀察者對象,調用了它們的update()方法 protected synchronized void clearChanged() { changed = false; } // 返回對象狀態是否改變 public synchronized boolean hasChanged() { return changed; } // 返回向量中觀察者的數量 public synchronized int countObservers() { return obs.size(); } }
相比較咱們本身的示例 Publisher
, java.util.Observer
中多了併發和 NPE 方面的考慮 。
使用 JDK 對觀察者模式的支持,改寫一下上面的示例:
增長一個通知類 WechatNotice
,用於推送通知的傳遞:
public class WechatNotice { private String publisher; private String articleName; // 省略 get/set }
而後改寫 WeChatClient
和 WeChatAccounts
,分別實現 JDK 的 Observer
接口和繼承 Observable
類:
public class WeChatClient implements Observer { private String username; public WeChatClient(String username) { this.username = username; } @Override public void update(Observable o, Object arg) { WechatNotice notice = (WechatNotice) arg; System.out.println(String.format("用戶<%s> 接收到 <%s>微信公衆號 的推送,文章標題爲 <%s>", username, notice.getPublisher(), notice.getArticleName())); } } public class WeChatAccounts extends Observable { private String name; public WeChatAccounts(String name) { this.name = name; } public void publishArticles(String articleName, String content) { System.out.println(String.format("\n<%s>微信公衆號 發佈了一篇推送,文章名稱爲 <%s>,內容爲 <%s> ", this.name, articleName, content)); setChanged(); notifyObservers(new WechatNotice(this.name, articleName)); } }
最後是一個測試類:
public class Test { public static void main(String[] args) { WeChatAccounts accounts = new WeChatAccounts("極客挖掘機"); WeChatClient user1 = new WeChatClient("張三"); WeChatClient user2 = new WeChatClient("李四"); WeChatClient user3 = new WeChatClient("王五"); accounts.addObserver(user1); accounts.addObserver(user2); accounts.addObserver(user3); accounts.publishArticles("設計模式 | 觀察者模式及典型應用", "觀察者模式的內容..."); accounts.deleteObserver(user1); accounts.publishArticles("設計模式 | 單例模式及典型應用", "單例模式的內容...."); } }
測試結果:
<極客挖掘機>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 觀察者模式及典型應用>,內容爲 <觀察者模式的內容...> 用戶<王五> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 用戶<李四> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> 用戶<張三> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 觀察者模式及典型應用> <極客挖掘機>微信公衆號 發佈了一篇推送,文章名稱爲 <設計模式 | 單例模式及典型應用>,內容爲 <單例模式的內容....> 用戶<王五> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用> 用戶<李四> 接收到 <極客挖掘機>微信公衆號 的推送,文章標題爲 <設計模式 | 單例模式及典型應用>
和前面的示例結果徹底一致。