「補課」進(jìn)行時:設(shè)計模式(15)——觀察者模式

1. 前文匯總
2. 觀察者模式
2.1 定義
觀察者模式(Observer Pattern)也叫做發(fā)布訂閱模式(Publish/subscribe),它是一個在項目中經(jīng)常使用的模式,其定義如下:
Define a one-to-many dependency between objects so that when oneobject changes state,all its dependents are notified and updatedautomatically.(定義對象間一種一對多的依賴關(guān)系,使得每當(dāng)一個對象改變狀態(tài),則所有依賴于它的對象都會得到通知并被自動更新。)
2.2 通用類圖

Subject 被觀察者: 定義被觀察者必須實現(xiàn)的職責(zé),它必須能夠動態(tài)地增加、取消觀察者。它一般是抽象類或者是實現(xiàn)類,僅僅完成作為被觀察者必須實現(xiàn)的職責(zé):管理觀察者并通知觀察者。 ConcreteSubject 具體的被觀察者: 定義被觀察者自己的業(yè)務(wù)邏輯,同時定義對哪些事件進(jìn)行通知。 Observer 觀察者: 觀察者接收到消息后,即進(jìn)行update(更新方法)操作,對接收到的信息進(jìn)行處理。 ConcreteObserver 具體的觀察者: 每個觀察在接收到消息后的處理反應(yīng)是不同,各個觀察者有自己的處理邏輯。
2.3 通用代碼
Subject 被觀察者:
public?abstract?class?Subject?{
????//?定義一個觀察者數(shù)組
????private?Vector?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()?{
????????//?具體的業(yè)務(wù)
????????super.notifyObservers();
????}
}
Observer 觀察者:
public?interface?Observer?{
????void?update();
}
ConcreteObserver 具體的觀察者:
public?class?ConcreteObserver?implements?Observer{
????@Override
????public?void?update()?{
????????System.out.println("進(jìn)行消息處理");
????}
}
測試場景類:
public?class?Test?{
????public?static?void?main(String[]?args)?{
????????//?創(chuàng)建一個被觀察者
????????ConcreteSubject?subject?=?new?ConcreteSubject();
????????//?創(chuàng)建一個觀察者
????????Observer?observer?=?new?ConcreteObserver();
????????//?觀察者觀察被觀察者
????????subject.addObserver(observer);
????????//?觀察者開始活動了
????????subject.doSomething();
????}
}
3. 一個案例
觀察者模式是設(shè)計模式中的超級模式,有關(guān)他的應(yīng)用隨處可見。
就比如說微信公眾號,我每天推送一篇博文內(nèi)容,訂閱的用戶都能夠在我發(fā)布推送之后及時接收到推送,方便地在手機端進(jìn)行閱讀。
訂閱者接口(觀察者)
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>微信公眾號?的推送,文章標(biāo)題為?<%s>",?username,?publisher,?articleName));
????}
}
一個微信客戶端(具體觀察者)
public?class?Publisher?{
????private?List?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;
????}
}
具體目標(biāo)
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>微信公眾號?發(fā)布了一篇推送,文章名稱為?<%s>,內(nèi)容為?<%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("設(shè)計模式?|?觀察者模式及典型應(yīng)用",?"觀察者模式的內(nèi)容...");
????????accounts.unsubscribe(user1);
????????accounts.publishArticles("設(shè)計模式?|?單例模式及典型應(yīng)用",?"單例模式的內(nèi)容....");
????}
}
測試結(jié)果
<極客挖掘機>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>,內(nèi)容為?<觀察者模式的內(nèi)容...>?
用戶<張三>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
用戶<李四>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
用戶<王五>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
<極客挖掘機>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計模式?|?單例模式及典型應(yīng)用>,內(nèi)容為?<單例模式的內(nèi)容....>?
用戶<李四>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?單例模式及典型應(yīng)用>
用戶<王五>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?單例模式及典型應(yīng)用>
4. JDK 對的觀察者模式的支持
觀察者模式在 Java 語言中的地位非常重要。在 JDK 的 java.util 包中,提供了 Observable 類以及 Observer 接口,它們構(gòu)成了JDK對觀察者模式的支持。
在 java.util.Observer 接口中,僅有一個 update(Observable o, Object arg) 方法,當(dāng)觀察目標(biāo)發(fā)生變化時被調(diào)用:
public?interface?Observer?{
????void?update(Observable?o,?Object?arg);
}
java.util.Observable 類則為目標(biāo)類:
public?class?Observable?{
????private?boolean?changed?=?false;
????private?Vector?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);
????}
????//?通知方法,用于在方法內(nèi)部循環(huán)調(diào)用向量中每一個觀察者的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();
????}
????//?該方法被調(diào)用后會設(shè)置一個boolean類型的內(nèi)部標(biāo)記變量changed的值為true,表示觀察目標(biāo)對象的狀態(tài)發(fā)生了變化
????protected?synchronized?void?setChanged()?{
????????changed?=?true;
????}
????//?用于將changed變量的值設(shè)為false,表示對象狀態(tài)不再發(fā)生改變或者已經(jīng)通知了所有的觀察者對象,調(diào)用了它們的update()方法
????protected?synchronized?void?clearChanged()?{
????????changed?=?false;
????}
????//?返回對象狀態(tài)是否改變
????public?synchronized?boolean?hasChanged()?{
????????return?changed;
????}
????//?返回向量中觀察者的數(shù)量
????public?synchronized?int?countObservers()?{
????????return?obs.size();
????}
}
相比較我們自己的示例 Publisher , java.util.Observer 中多了并發(fā)和 NPE 方面的考慮 。
使用 JDK 對觀察者模式的支持,改寫一下上面的示例:
增加一個通知類 WechatNotice ,用于推送通知的傳遞:
public?class?WechatNotice?{
????private?String?publisher;
????private?String?articleName;
????//?省略?get/set
}
然后改寫 WeChatClient 和 WeChatAccounts ,分別實現(xiàn) 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>微信公眾號?的推送,文章標(biāo)題為?<%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>微信公眾號?發(fā)布了一篇推送,文章名稱為?<%s>,內(nèi)容為?<%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("設(shè)計模式?|?觀察者模式及典型應(yīng)用",?"觀察者模式的內(nèi)容...");
????????accounts.deleteObserver(user1);
????????accounts.publishArticles("設(shè)計模式?|?單例模式及典型應(yīng)用",?"單例模式的內(nèi)容....");
????}
}
測試結(jié)果:
<極客挖掘機>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>,內(nèi)容為?<觀察者模式的內(nèi)容...>?
用戶<王五>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
用戶<李四>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
用戶<張三>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?觀察者模式及典型應(yīng)用>
<極客挖掘機>微信公眾號?發(fā)布了一篇推送,文章名稱為?<設(shè)計模式?|?單例模式及典型應(yīng)用>,內(nèi)容為?<單例模式的內(nèi)容....>?
用戶<王五>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?單例模式及典型應(yīng)用>
用戶<李四>?接收到?<極客挖掘機>微信公眾號?的推送,文章標(biāo)題為?<設(shè)計模式?|?單例模式及典型應(yīng)用>
和前面的示例結(jié)果完全一致。

