1. 前文匯總
2. 觀察者模式
2.1 定義
觀察者模式(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.(定義對象間一種一對多的依賴關系,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新。)
2.2 通用類圖
- Subject 被觀察者: 定義被觀察者必須實現的職責,它必須能夠動態地增加、取消觀察者。它一般是抽象類或者是實現類,僅僅完成作為被觀察者必須實現的職責:管理觀察者並通知觀察者。
- ConcreteSubject 具體的被觀察者: 定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知。
- Observer 觀察者: 觀察者接收到消息后,即進行update(更新方法)操作,對接收到的信息進行處理。
- ConcreteObserver 具體的觀察者: 每個觀察在接收到消息后的處理反應是不同,各個觀察者有自己的處理邏輯。
2.3 通用代碼
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 觀察者:
public interface Observer {
void update();
}
ConcreteObserver 具體的觀察者:
public class ConcreteObserver implements Observer{
@Override
public void update() {
System.out.println("進行消息處理");
}
}
測試場景類:
public class Test {
public static void main(String[] args) {
// 創建一個被觀察者
ConcreteSubject subject = new ConcreteSubject();
// 創建一個觀察者
Observer observer = new ConcreteObserver();
// 觀察者觀察被觀察者
subject.addObserver(observer);
// 觀察者開始活動了
subject.doSomething();
}
}
3. 一個案例
觀察者模式是設計模式中的超級模式,有關他的應用隨處可見。
就比如說微信公眾號,我每天推送一篇博文內容,訂閱的用戶都能夠在我發布推送之后及時接收到推送,方便地在手機端進行閱讀。
訂閱者接口(觀察者)
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("設計模式 | 單例模式及典型應用", "單例模式的內容....");
}
}
測試結果
<極客挖掘機>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 觀察者模式及典型應用>,內容為 <觀察者模式的內容...>
用戶<張三> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
用戶<李四> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
用戶<王五> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
<極客挖掘機>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 單例模式及典型應用>,內容為 <單例模式的內容....>
用戶<李四> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>
用戶<王五> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>
4. JDK 對的觀察者模式的支持
觀察者模式在 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("設計模式 | 單例模式及典型應用", "單例模式的內容....");
}
}
測試結果:
<極客挖掘機>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 觀察者模式及典型應用>,內容為 <觀察者模式的內容...>
用戶<王五> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
用戶<李四> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
用戶<張三> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 觀察者模式及典型應用>
<極客挖掘機>微信公眾號 發布了一篇推送,文章名稱為 <設計模式 | 單例模式及典型應用>,內容為 <單例模式的內容....>
用戶<王五> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>
用戶<李四> 接收到 <極客挖掘機>微信公眾號 的推送,文章標題為 <設計模式 | 單例模式及典型應用>
和前面的示例結果完全一致。