本文參照《Head First 設(shè)計(jì)模式》,轉(zhuǎn)載請(qǐng)注明出處
對(duì)于整個(gè)系列,我們按照這本書的設(shè)計(jì)邏輯怒允,使用情景分析的方式來描述,并且穿插使用一些問題锈遥,總結(jié)的方式來講述误算。并且所有的開發(fā)源碼仰美,都會(huì)托管到github上。
項(xiàng)目地址:https://github.com/jixiang52002/HFDesignPattern
前一章主要講解了設(shè)計(jì)模式入門和最常用的一個(gè)模式-----策略模式儿礼,并結(jié)合Joe的鴨子模型進(jìn)行分析咖杂,想要了解的朋友可以回去回看一下。Head First 設(shè)計(jì)模式(1)-----策略模式
這里我們將繼續(xù)介紹一種可以幫助對(duì)象知悉現(xiàn)狀蚊夫,不會(huì)錯(cuò)過該對(duì)象感興趣的事诉字。甚至對(duì)象可以自己決定是都要繼續(xù)接受通知。有過設(shè)計(jì)模式學(xué)習(xí)經(jīng)驗(yàn)的人會(huì)脫口而出-----觀察者模式知纷。對(duì)的壤圃,接下來我們將了解一個(gè)新的設(shè)計(jì)模式,也就是觀察者模式琅轧。
1.引言
最近你的團(tuán)隊(duì)獲取了一個(gè)新的合約伍绳,需要負(fù)責(zé)建立一個(gè)Weather-O-Rama公司的下一代氣象站----Internet氣象觀測(cè)站。
合約內(nèi)容如下:
恭喜貴公司獲選為敝公司建立下一代Internet氣象觀測(cè)站乍桂!該氣象站必須建立在我們專利申請(qǐng)的WeatherData對(duì)象上冲杀,由WeatherData對(duì)象負(fù)責(zé)追蹤目前的天氣狀況(溫度、濕度睹酌、氣壓)权谁。我們希望貴公司能建立一個(gè)應(yīng)用,有三種布告板憋沿,分別顯示目前的狀況旺芽、氣象統(tǒng)計(jì)及簡單的預(yù)報(bào)。當(dāng)WeatherData對(duì)象獲取到最新的測(cè)量數(shù)據(jù)時(shí)辐啄,三種布告板必須實(shí)時(shí)更新采章。
而且,這是一個(gè)可以拓展的氣象站壶辜,Weather-O-Rama氣象站希望公布一組API悯舟,讓其他開發(fā)人員可以寫出自己的氣象布告板,并插入此應(yīng)用中我們希望貴公司可以提供這樣的API士复。
Weather-O-Rama氣象站有很好的商業(yè)運(yùn)營模式:一旦客戶上鉤,他們使用每個(gè)布告板都要付錢最好的部分就是翩活,為了感謝貴公司建立此系統(tǒng)阱洪,我們將以公司的認(rèn)股權(quán)支付你。
我們期待看到你的設(shè)計(jì)和應(yīng)用的alpha版本菠镇。
附注:我們正在通宵整理WeatherData源文件給你們冗荸。
1.1需求分析
根據(jù)開發(fā)的經(jīng)驗(yàn),我們首先分析Weather-O-Rama公司的需求:
- 此系統(tǒng)有三個(gè)部分組成:氣象站(獲取實(shí)際的氣象數(shù)據(jù)的物理組成)利耍,WeatherData對(duì)象(追蹤來自氣象站的數(shù)據(jù)蚌本,并更新布告板)和布告板(顯示目前天氣狀況展示給用戶)
- 項(xiàng)目應(yīng)用中盔粹,開發(fā)者需要利用WeatherData去實(shí)時(shí)獲取氣象數(shù)據(jù),并且更新三個(gè)布告板:目前氣象程癌,氣象統(tǒng)計(jì)和天氣預(yù)報(bào)舷嗡。
- 系統(tǒng)必須具備很高的可拓展性,讓其他的開發(fā)人員可以建立定制的布告板嵌莉,用戶可以隨心所欲地添加或刪除任何布告板进萄。
我們初始設(shè)計(jì)結(jié)構(gòu)如下:
1.2WeatherData類
第二天,Weather-O-Rama公司發(fā)送過來WeatherData的源碼锐峭,其結(jié)構(gòu)如下圖
其中measurementsChanged()方法在氣象測(cè)試更新時(shí)中鼠,被調(diào)用。
1.3錯(cuò)誤的編碼方式
首先沿癞,我們從大部分不懂設(shè)計(jì)模式的開發(fā)者常用的設(shè)計(jì)方式開始援雇。
根據(jù)Weather-O-Rama氣象站開發(fā)人員的需求暗示,在measurementsChanged()方法中添加相關(guān)的代碼:
public class WeatherData {
private float temperature;//溫度
private float humidity;//濕度
private float pressure;//氣壓
private CurrentConditionsDisplay currentConditionsDisplay;//目前狀態(tài)布告板
private StatisticsDisplay statisticsDisplay;//統(tǒng)計(jì)布告板
private ForecastDisplay forecastDisplay;//預(yù)測(cè)布告板
public WeatherData(CurrentConditionsDisplay currentConditionsDisplay
,StatisticsDisplay statisticsDisplay
,ForecastDisplay forecastDisplay){
this.currentConditionsDisplay=currentConditionsDisplay;
this.statisticsDisplay=statisticsDisplay;
this.forecastDisplay=forecastDisplay;
}
public float getTemperature() {
return temperature;
}
public float getHumidity(){
return humidity;
}
public float getPressure(){
return pressure;
}
//實(shí)例變量聲明
public void measurementsChanged(){
//調(diào)用WeatherData的三個(gè)getter方法獲取最近的測(cè)量值
float temp=getTemperature();
float humidity=getHumidity();
float pressure=getPressure();
currentConditionsDisplay.update(temp,humidity,pressure);
statisticsDisplay.update(temp,humidity,pressure);
forecastDisplay.update(temp,humidity,pressure);
}
//通知發(fā)生變化
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
回顧第一章的三個(gè)設(shè)計(jì)原則椎扬,我們發(fā)現(xiàn)這里違反了幾個(gè)原則
第一設(shè)計(jì)原則
找出應(yīng)用中可能需要變化之處惫搏,把它們獨(dú)立出來,不要和那些不需要變化的代碼混合在一起晶府。
第二設(shè)計(jì)原則
針對(duì)于接口編程钻趋,不針對(duì)實(shí)現(xiàn)編程
第三設(shè)計(jì)原則
多用組合,少用繼承
在這里我們使用了針對(duì)實(shí)現(xiàn)編程蛮位,并且沒有將變化部分獨(dú)立出來较沪,這樣會(huì)導(dǎo)致我們以后在增加或刪除布告板時(shí)必須修改應(yīng)用程序失仁。而且,最重要的是萄焦,我們犧牲了可拓展性控轿。
既然這里我們提到了要使用觀察者模式來解決問題拂封,那么該如何下手。并且冒签,什么是觀察者模式萧恕?
2.觀察者模式
2.1認(rèn)識(shí)觀察者模式
為了方便理解肠阱,我們從日常生活中常遇到的情形來理解觀察者模式朴读,這里我們使用生活常見的報(bào)紙和雜志訂閱業(yè)務(wù)邏輯來理解:
- 報(bào)社的業(yè)務(wù)在于出版報(bào)紙
- 訂閱報(bào)紙的用戶,只要對(duì)應(yīng)報(bào)社有新的報(bào)紙出版缘回,就會(huì)給你送來
- 當(dāng)用戶不想繼續(xù)訂閱報(bào)紙典挑,可以直接取消訂閱。那么之后就算有新的報(bào)紙出版拙寡,也不會(huì)送給對(duì)應(yīng)用戶了琳水。
- 只要報(bào)社一直存在,任何用戶都可以自由訂閱或取消訂閱報(bào)紙
從上面的邏輯我們分析出诚啃,這里由以下部分組成私沮,報(bào)社,用戶造垛,訂閱晰搀。將其抽象出來就i是:出版者,訂閱者杆逗,訂閱鳞疲。這里觀察者模式的雛形已經(jīng)出來了。
出版者+訂閱者=觀察者模式
如果上面已經(jīng)理解了報(bào)社報(bào)紙訂閱的邏輯排龄,也可以很快知道觀察者模式是什么翎朱。只是在其中名稱會(huì)有差異,前面提到的“出版者”我們可以稱為“主題(Subject)”或“被觀察者(Observable)”(后一個(gè)更加常用)拴曲,“訂閱者”我們稱為“觀察者(Observer)”澈灼,這里我們采用類UML的結(jié)構(gòu)圖來解釋:
2.2 觀察者模式注冊(cè)/取消注冊(cè)
場(chǎng)景1:
某一天,鴨子對(duì)象覺得自己的朋友都訂閱了主題委乌,自己也想稱為一個(gè)觀察者荣回。于是告訴主題遭贸,它想當(dāng)一個(gè)觀察者壕吹。完成訂閱后删铃,鴨子也成為一個(gè)觀察者了。
這樣當(dāng)主題數(shù)據(jù)發(fā)生變化時(shí)咒劲,鴨子對(duì)象也可以得到通知了!缎患!
場(chǎng)景2:
老鼠對(duì)象厭煩了每天都被主題煩挤渔,決定從觀察者序列離開风题,于是它告訴主題它想離開觀察者行列,主題將它從觀察者中除名眼刃。
之后主題數(shù)據(jù)發(fā)生變化時(shí)摇肌,不會(huì)再通知老鼠對(duì)象围小。
上面的兩個(gè)情形分別對(duì)應(yīng)了注冊(cè)和取消注冊(cè)树碱,這也是觀察者模式最重要的兩個(gè)概念变秦。注冊(cè)后的對(duì)象我們才可以稱為觀察者。觀察者取消注冊(cè)后也不能稱為觀察者赎婚。
2.3 觀察者模式定義
通過報(bào)紙業(yè)務(wù)和對(duì)象訂閱的例子樱溉,我們可以勾勒出觀察者模式的基本概念。
觀察者模式定義了對(duì)象之間的一對(duì)多的依賴歧焦,這樣一來肚医,當(dāng)一個(gè)對(duì)象改變狀態(tài)時(shí)肠套,它所有的依賴者都會(huì)收到通知并自動(dòng)更新。
主題/被觀察者和觀察者之間定義了一對(duì)多的關(guān)系瓷耙。觀察者依賴于主題/被觀察者刁赖。一旦主題/被觀察者數(shù)據(jù)發(fā)生改變的時(shí)候宇弛,觀察者就會(huì)收到通知。那么彻况,如何實(shí)現(xiàn)觀察者和主題/被觀察者呢舅踪?
2.4 觀察者模式實(shí)現(xiàn)
由于網(wǎng)絡(luò)上的實(shí)現(xiàn)觀察者的方式非常多抽碌,我們這里采取比較容易理解的方式Subject和Observer。對(duì)于更高級(jí)的使用方式左权,可以百度。
接下來我們來看看基于Subject和Observer的類圖結(jié)構(gòu):
3. 設(shè)計(jì)氣象站
到這里我們?cè)倩氐疆?dāng)初的問題,氣象站中結(jié)構(gòu)模型為一對(duì)多模型瀑梗,其中WeatherData為氣象模型中的“一”抛丽,而“多”也就對(duì)應(yīng)了這里用來展示天氣監(jiān)測(cè)數(shù)據(jù)的各種布告板饰豺。相對(duì)于之前的針對(duì)實(shí)現(xiàn)的方式冤吨,使用觀察者模式來設(shè)計(jì)會(huì)更加符合需求。優(yōu)先我們給出新的氣象站模型垒探。
3.1實(shí)現(xiàn)氣象站
依照前面的設(shè)計(jì)結(jié)構(gòu)圖怠李,最終來實(shí)現(xiàn)具體代碼結(jié)構(gòu)
1.Subject
public interface Subject {
//注冊(cè)觀察者
public void registerObserver(Observer o);
//刪除觀察者
public void removeObserver(Observer o);
//當(dāng)主題發(fā)生數(shù)據(jù)變化時(shí)捺癞,通知所有觀察
public void notifyObservers();
}
2.Observer
public interface Observer {
/**
*
* update:當(dāng)氣象站的觀測(cè)數(shù)據(jù)發(fā)生改變時(shí),這個(gè)方法會(huì)被調(diào)用
* @param temp 溫度
* @param hunmidity 濕度
* @param pressure 氣壓
* @since JDK 1.6
*/
public void update(float temp,float hunmidity,float pressure);
}
3.DisplayElement
public interface DisplayElement {
//當(dāng)布告板需要展示時(shí)惕鼓,調(diào)用此方法時(shí)
public void display();
}
4.新的WeatherData1
public class WeatherData1 implements Subject{
private ArrayList<Observer> observers;
private float temperature;
private float humiditty;
private float pressure;
public WeatherData1(){
observers=new ArrayList<Observer>();
}
//注冊(cè)
public void registerObserver(Observer o) {
observers.add(o);
}
//刪除
public void removeObserver(Observer o) {
int i=observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
//通知觀察者數(shù)據(jù)變化
public void notifyObservers() {
for(int i=0;i<observers.size();i++){
Observer observer=observers.get(i);
observer.update(temperature, humiditty, pressure);
}
}
public void measurementsChanged(){
notifyObservers();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humiditty=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
5.CurrentConditionsDisplay
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
/**
*
* update:更新布告板內(nèi)容
* @author 吉祥
* @param temperature
* @param humidity
* @param pressure
* @since JDK 1.6
*/
public void update(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Current conditons:"+temperature
+"F degrees and "+humidity+"% humidity");
}
}
6.ForecastDisplay
public class ForecastDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public ForecastDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
/**
*
* update:更新布告板內(nèi)容
* @author 吉祥
* @param temperature
* @param humidity
* @param pressure
* @since JDK 1.6
*/
public void update(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Forecast: More of the same");
}
}
7.StatisticsDisplay
public class StatisticsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
/**
*
* update:更新布告板內(nèi)容
* @author 吉祥
* @param temperature
* @param humidity
* @param pressure
* @since JDK 1.6
*/
public void update(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Avg/Max/Min temperature= "+temperature
+"/"+temperature+"/"+temperature);
}
}
ps:這里在Observer中使用Subject原因在于方便以后的取消注冊(cè)彻犁。
最后我們建立一個(gè)測(cè)試類WeatherStation來進(jìn)行測(cè)試
public class WeatherStation {
public static void main(String[] args){
WeatherData1 weatherData=new WeatherData1();
CurrentConditionsDisplay currentConditionsDisplay=new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay=new ForecastDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
最終結(jié)果如下
到這里我們已經(jīng)講解完觀察者模式的一種實(shí)現(xiàn)方式汞幢。但是這我們也提出一個(gè)問題,用來發(fā)散输钩。
是否能夠在主題中提供向外的可以讓觀察者自己獲取自己想要數(shù)據(jù),而并非將所有的數(shù)據(jù)都推送給觀察者姻氨?也就是在Push(推)的同時(shí)我們也可以pull(拉)剪验。
4.Java內(nèi)置的觀察者模式
剛才的問題功戚,其實(shí)熟悉Java語言的開發(fā)者會(huì)發(fā)現(xiàn),在Java中已經(jīng)有相應(yīng)的模式届宠,如果熟悉的可以直接跳過本章乘粒。
在java.util包下有Observer和Observable類灯萍,這兩個(gè)類的結(jié)構(gòu)跟我們遇到的Subject和Observer模型有些類似。甚至可是隨意使用push(推)或者pull(拉)
這里我們使用在線的Java API網(wǎng)站在線Java API文檔
首先查詢Observer的API
這個(gè)與我們所寫的Observer結(jié)構(gòu)幾乎相同属桦,只是在推送是把Observable類一起推送聂宾,這樣用戶既可以push也可以使用pull的方式诊笤。那么Observable的結(jié)構(gòu)呢
我們發(fā)現(xiàn)這里Observable是類與我們之前Subject作為接口的方式稍微有區(qū)別讨跟;并且Observable類其他方法更全。那么使用類的方式和使用接口的影響我們?cè)诤竺鏁?huì)繼續(xù)講茶袒。并且這里我們關(guān)注setChanged()方法告訴被觀察者的數(shù)據(jù)發(fā)生改變
那么凉馆,如果要使用Java中自帶的觀察者模式來修改原有氣象站業(yè)務(wù)會(huì)如何。
首先向叉,我們來分析更改后氣象站的模型:
4.1Java內(nèi)置觀察者模式運(yùn)作模式
相對(duì)于于之前Subject和Observer的模式母谎,Java內(nèi)置自帶的觀察者模式運(yùn)行稍微有些差異奇唤。
將對(duì)象變成觀察者只需要實(shí)現(xiàn)Observer(java.util.Observer)接口,然后調(diào)用任何Observable的addObserver()方法即可。如果要?jiǎng)h除觀察者来惧,調(diào)用deleteObserver()即可供搀。
被觀察者若要推送通知,需要對(duì)象繼承Observable(java.util.Observable)類胎源,并先調(diào)用setChanged(),首先標(biāo)記狀態(tài)已經(jīng)改變屿脐。然后調(diào)用notifyObservers()方法中的一個(gè):notifyObservers()(通知觀察者pull數(shù)據(jù))或notifyObserers(Object object)(通知觀察者push數(shù)據(jù))
那么作為觀察者如何處理被觀察者推送出的數(shù)據(jù)呢的诵。
這里邏輯如下:
- 觀察者(Observer)必須在update(Observable o,Object object).前一個(gè)參數(shù)用來讓觀察者知道是哪個(gè)被觀察者推送數(shù)據(jù)。后一個(gè)object為推送數(shù)據(jù)烦粒,允許為null代赁。
4.2 setChanged()
在Observable類中setChanged()方法一開始我也有疑惑芭碍,為何在推送之前需要調(diào)用該方法。后來查閱資料和Java API發(fā)現(xiàn)它很好的一個(gè)用處廉涕。我們先來查看java的源碼
這里必須標(biāo)記為true才會(huì)推送消息狐蜕,那么這個(gè)到底有何好處,我們拿氣象站模型來分析婆瓜。
如果沒有setChanged方法贡羔,也是之前的Subject和Observer模型里乖寒,一旦數(shù)據(jù)發(fā)生細(xì)微的變化,我們都會(huì)對(duì)所有的觀察者進(jìn)行推送磅轻。如果我們需要在溫度變化1攝氏度以上才發(fā)送推送逐虚,調(diào)用setChanged()方法更加有效。當(dāng)然撮躁,這個(gè)功能使用場(chǎng)景很少把曼,但是也不排除會(huì)用到漓穿。當(dāng)然更改Object和Observer模型也是可以做到這個(gè)效果的!P亡ā山害!
4.3 Java內(nèi)置觀察者更改氣象站
那么利用氣象站模型來實(shí)際操作一下浪慌,依照之前的模型我們代碼應(yīng)該如下
1.WeatherData2
public class WeatherData2 extends Observable{
private float temperature;
private float humidity;
private float pressure;
//構(gòu)造器不需要為了記住觀察者建立數(shù)據(jù)模型
public WeatherData2(){
}
public void measurementsChanged(){
//在調(diào)用notifyObserver()需要指示狀態(tài)已經(jīng)更改了
setChanged();
//這里未使用notifyObserver(object),所以數(shù)據(jù)采用拉的邏輯
notifyObservers(this);
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
//以下方法為pull操作提供
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
2.CurrentConditionsDisplay1
public class CurrentConditionsDisplay1 implements Observer,DisplayElement{
private Observable observable;
private float temperature;
private float humidity;
private float pressure;
//構(gòu)造器需要傳入Observable參數(shù)权纤,并登記成為觀察者
public CurrentConditionsDisplay1(Observable observable){
this.observable=observable;
observable.addObserver(this);
}
//update方法增加Observable和數(shù)據(jù)對(duì)象作為參數(shù)
public void update(Observable o, Object arg) {
if(arg instanceof WeatherData2){
WeatherData2 weatherData2=(WeatherData2) arg;
this.temperature=weatherData2.getTemperature();
this.humidity=weatherData2.getHumidity();
this.pressure=weatherData2.getPressure();
display();
}
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Current conditons:"+temperature
+"F degrees and "+humidity+"% humidity");
}
}
3.ForecastDisplay1
public class ForecastDisplay1 implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Observable observable;
public ForecastDisplay1(Observable observable){
this.observable=observable;
observable.addObserver(this);
}
public void update(Observable o,Object arg){
if(arg instanceof WeatherData2){
WeatherData2 weatherData2=(WeatherData2) arg;
this.temperature=weatherData2.getTemperature();
this.humidity=weatherData2.getHumidity();
this.pressure=weatherData2.getPressure();
display();
}
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Forecast: More of the same");
}
}
4.StatisticsDisplay1
public class StatisticsDisplay1 implements Observer,DisplayElement{
private float temperature;
private float humidity;
private float pressure;
private Observable observable;
public StatisticsDisplay1(Observable observable){
this.observable=observable;
observable.addObserver(this);
}
public void update(Observable o,Object arg){
if(arg instanceof WeatherData2){
WeatherData2 weatherData2=(WeatherData2) arg;
this.temperature=weatherData2.getTemperature();
this.humidity=weatherData2.getHumidity();
this.pressure=weatherData2.getPressure();
display();
}
}
/**
*
* display:展示布告板內(nèi)容
* @author 吉祥
* @since JDK 1.6
*/
public void display(){
System.out.println("Avg/Max/Min temperature= "+temperature
+"/"+temperature+"/"+temperature);
}
}
最后進(jìn)行測(cè)試:
WeatherStation1
public class WeatherStation1 {
public static void main(String[] args){
WeatherData2 weatherData=new WeatherData2();
CurrentConditionsDisplay1 currentConditionsDisplay=new CurrentConditionsDisplay1(weatherData);
StatisticsDisplay1 statisticsDisplay=new StatisticsDisplay1(weatherData);
ForecastDisplay1 forecastDisplay=new ForecastDisplay1(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
結(jié)果最終如下:
我們對(duì)比之前Subject和Observer的觀察者模式會(huì)發(fā)現(xiàn)兩者輸出順序不一樣外邓,這是為什么损话?
其實(shí)java.util.Observable不依賴于觀察者被通知的順序的,并且實(shí)現(xiàn)了他的notifyObserver()方法光涂,這會(huì)導(dǎo)致通知觀察者的順序不同于Subject和Observer模型在具體類實(shí)現(xiàn)notifyObserver()方法拧烦。其實(shí)兩者都沒有任何的代碼誤差恋博,只是實(shí)現(xiàn)的方式不同導(dǎo)致不同的結(jié)果。
但是java.util.Observable類卻違背了之前第一章中針對(duì)接口編程重虑,而非針對(duì)實(shí)現(xiàn)編程秦士∮栏撸恐怖的是命爬,它也沒有接口實(shí)現(xiàn),這就導(dǎo)致它的使用具有很高的局限性和低復(fù)用性皆愉。如果一個(gè)對(duì)象不僅僅是被觀察者艇抠,同時(shí)還是另一個(gè)超類的子類的時(shí)候,我們無法使用多繼承的方式來實(shí)現(xiàn)异剥。我們?nèi)绻孕型卣沟脑捫踔兀銜?huì)發(fā)現(xiàn)setChanged()方法是protected方法,這就表示只有java.util.Observable自身和其子類才可以使用這個(gè)方法督怜。這就違反了第二個(gè)設(shè)計(jì)原則---------"多用組合亮蛔,少用繼承"。這也是我一般不會(huì)使用Java自帶的設(shè)計(jì)者模式的原因辣吃。
現(xiàn)在比較流行的觀察者模式芬探,也就是RxJava偷仿,但是由于這個(gè)框架涉及不僅僅有觀察這模式,在之后整個(gè)設(shè)計(jì)模式整理玩不后节榜,我會(huì)集中再講别智。
5.總結(jié)
到此,觀察者模式的講解已經(jīng)全部講解完成讳窟±龇龋總結(jié)一下硬猫。
第四設(shè)計(jì)原則
為交互對(duì)象之間的松耦合涉及而努力
觀察者模式
在對(duì)象之間定義一對(duì)多的依賴,這樣一來坑雅,當(dāng)一個(gè)對(duì)象改變狀態(tài)盔性,依賴它的對(duì)象都會(huì)收到通知冕香,并自動(dòng)更新后豫。
相應(yīng)的資料和代碼托管地址https://github.com/jixiang52002/HFDesignPattern