观察者模式重复调用mysql问题,2、观察者模式
整理: 氣象站的故事 現(xiàn)在我們要為一家氣象站開(kāi)發(fā)一套氣象監(jiān)控系統(tǒng),按照客戶的要求,這個(gè)監(jiān)控系統(tǒng)必須可以實(shí)時(shí)跟蹤當(dāng)前的天氣狀況(溫度、濕度、大氣壓力),并且可以在三種不同設(shè)備上顯示出來(lái)(當(dāng)前天氣狀況、天氣統(tǒng)計(jì)、天氣預(yù)測(cè))。客戶還希望這個(gè)系統(tǒng)可以對(duì)外
整理:
氣象站的故事
現(xiàn)在我們要為一家氣象站開(kāi)發(fā)一套氣象監(jiān)控系統(tǒng),按照客戶的要求,這個(gè)監(jiān)控系統(tǒng)必須可以實(shí)時(shí)跟蹤當(dāng)前的天氣狀況(溫度、濕度、大氣壓力),并且可以在三種不同設(shè)備上顯示出來(lái)(當(dāng)前天氣狀況、天氣統(tǒng)計(jì)、天氣預(yù)測(cè))。客戶還希望這個(gè)系統(tǒng)可以對(duì)外提供一個(gè)API接口,以便任何開(kāi)發(fā)者都可以開(kāi)發(fā)自己的顯示設(shè)備,然后無(wú)縫掛接到系統(tǒng)中,系統(tǒng)可以統(tǒng)一更新所有顯示設(shè)備的數(shù)據(jù)。客戶還會(huì)提供一個(gè)可以訪問(wèn)氣象站的硬件設(shè)備的組件,如下圖所示:
它提供了三個(gè)方法(get開(kāi)頭),可以分別取得實(shí)時(shí)的溫度、濕度和大氣壓力,還有一個(gè)MeasurementsChanged()方法,當(dāng)任何天氣狀況發(fā)生變化的時(shí)候,這個(gè)方法都會(huì)自動(dòng)被觸發(fā),當(dāng)前這個(gè)方法只是一個(gè)空函數(shù),擴(kuò)展的代碼還需要我們自己去擴(kuò)充。至于WeatherData是如何取得天氣狀況的,還有MeasurementsChanged()方法是如何被自動(dòng)觸發(fā)的這些事情都不需要我們?nèi)タ紤],我們只管考慮如果做好跟顯示設(shè)備有關(guān)的事情就好了。
OK!讓我們來(lái)考慮一下這個(gè)系統(tǒng)的實(shí)現(xiàn),先重新理一下思路:
1. 客戶提供了獲取實(shí)時(shí)的天氣狀況的方法。
2. MeasurementsChanged()方法會(huì)在天氣狀況變化時(shí)被自動(dòng)調(diào)用。
3. 系統(tǒng)要實(shí)現(xiàn)三種顯示模式,分別顯示天氣狀況、天氣統(tǒng)計(jì)和天氣預(yù)測(cè),而且這些顯示的信息必須跟當(dāng)前最新的天氣狀況實(shí)時(shí)同步。
4. 系統(tǒng)還必須支持在顯示方式上的擴(kuò)展性,而且使用者可以任意添加和移除不同的顯示模式。
基于上面這些信息,我們大概都會(huì)想到可以象下面這樣來(lái)實(shí)現(xiàn)這個(gè)系統(tǒng):
//偽代碼
public class WeatherData
{
//實(shí)例化顯示設(shè)備(省略)
public void MeasurementsChanged()
{
float temp = getTemperature(); //取得溫度
float humidity = getHumidity(); //取得濕度
float pressure = getPressure(); //取得氣壓
currentConditionsDisplay.update(temp, humidity, pressure); //同步顯示當(dāng)前天氣狀況
statisticsDisplay.update(temp, humidity, pressure); //同步顯示天氣統(tǒng)計(jì)信息
forecastDisplay.update(temp, humidity, pressure); //同步顯示天氣預(yù)報(bào)信息
}
}
因?yàn)榭蛻粢呀?jīng)給我們提供了實(shí)時(shí)的數(shù)據(jù),還提供了數(shù)據(jù)更新時(shí)候的觸發(fā)機(jī)制,那么我們要做的就是把最新的數(shù)據(jù)提供給不同的顯示設(shè)備就OK了,上面的代碼好象已經(jīng)可以基本解決問(wèn)題啦。哈哈!
面向接口編程,而不要面向?qū)崿F(xiàn)編程。”的原則,這樣實(shí)現(xiàn)會(huì)帶來(lái)的問(wèn)題是系統(tǒng)無(wú)法滿足在不修改代碼的情況下動(dòng)態(tài)添加或移除不同的顯示設(shè)備。換句話說(shuō),顯示設(shè)備相關(guān)的部分是系統(tǒng)中最不穩(wěn)定的部分,應(yīng)該將其單獨(dú)隔離開(kāi),也就是前面學(xué)過(guò)的另一個(gè)原則:“找到系統(tǒng)中變化的部分,將變化的部分同其它穩(wěn)定的部分隔開(kāi)。”那么我們到底該怎么辦呢?呵呵,既然這篇文章是講觀察者模式的,當(dāng)然要用它來(lái)結(jié)束戰(zhàn)斗!下面我們先來(lái)認(rèn)識(shí)一下觀察者模式~
這就是觀察者模式
我們還是先看一下官方的定義:
The Observer Patterndefines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically. (觀察者模式定義了對(duì)象間的一種一對(duì)多依賴(lài)關(guān)系,使得每當(dāng)一個(gè)對(duì)象改變狀態(tài),則所有依賴(lài)于它的對(duì)象都會(huì)得到通知并被自動(dòng)更新)
咋樣?這是超級(jí)經(jīng)典的標(biāo)準(zhǔn)定義,如假抱換的!不懂?那再看看下面的類(lèi)圖吧~
Subject(被觀察的對(duì)象接口)
l 規(guī)定ConcreteSubject的統(tǒng)一接口;
l 每個(gè)Subject可以有多個(gè)Observer;
ConcreteSubject(具體被觀察對(duì)象)
l 維護(hù)對(duì)所有具體觀察者的引用的列表;
l 狀態(tài)發(fā)生變化時(shí)會(huì)發(fā)送通知給所有注冊(cè)的觀察者。
Observer(觀察者接口)
l 規(guī)定ConcreteObserver的統(tǒng)一接口;
l 定義了一個(gè)update()方法,在被觀察對(duì)象狀態(tài)改變時(shí)會(huì)被調(diào)用。
ConcreteObserver(具體觀察者)
l 維護(hù)一個(gè)對(duì)ConcreteSubject的引用;
l 特定狀態(tài)與ConcreteSubject同步;
l 實(shí)現(xiàn)Observer接口,通過(guò)update()方法接收ConcreteSubject的通知。
怎么樣,現(xiàn)在總該有點(diǎn)感覺(jué)了吧?下面還有一個(gè)順序圖,再體會(huì)體會(huì)~
呵呵!還沒(méi)想明白,為什么官方的東西總是看不懂,看來(lái)是沒(méi)當(dāng)官的命啦!其實(shí)觀察者模式十分簡(jiǎn)單,現(xiàn)實(shí)生活中的例子更是隨處可見(jiàn),就比如看電視:某個(gè)觀眾就是一個(gè)標(biāo)準(zhǔn)的ConcreteObserver(具體觀察者,都符合統(tǒng)一的Observer接口,即都要通過(guò)電視收看節(jié)目的觀眾),電視節(jié)目就是Subject(被觀察對(duì)象接口,這里體現(xiàn)為無(wú)線電視信號(hào))了,不同的頻道的節(jié)目是不同的ConcreteSubject(不同頻道有不同的節(jié)目),觀眾可以自由決定看電視(registerObserver)或不看電視(removeObserver),而電視節(jié)目的變化也會(huì)在自動(dòng)更新(notifyObservers)所有觀眾的收看內(nèi)容。怎么樣?這回明白了吧!
另外觀察者模式也叫發(fā)布-訂閱模式(Publishers + Subscribers = Observer Pattern),跟看電視一樣,訂閱報(bào)紙也是一個(gè)很直觀的例子,有人發(fā)布(Publish = Subject)報(bào)紙,有人訂閱(Subscribe = Observer)報(bào)紙,訂閱的人可以定期收到最新發(fā)布的報(bào)紙,訂閱人也可以隨時(shí)退訂。
現(xiàn)在大家應(yīng)該對(duì)觀察者模式基本都了解了,我們來(lái)用這個(gè)模式來(lái)解決氣象站哪個(gè)問(wèn)題。就氣象站問(wèn)題的應(yīng)用場(chǎng)景來(lái)說(shuō),WeatherData可以作為ConcreteSubject來(lái)看待,而不同的顯示設(shè)備則可以作為ConcreteObserver來(lái)看待,也就是說(shuō)顯示設(shè)備觀察WeatherData對(duì)象,如果WeatherData對(duì)象有任何狀態(tài)變化,則立刻更新顯示設(shè)備的數(shù)據(jù)信息。這么說(shuō)似乎很靠譜了,下面我們?cè)賮?lái)具體實(shí)現(xiàn)一下吧,先從整體結(jié)構(gòu)開(kāi)始,如下類(lèi)圖:
跟前面說(shuō)的實(shí)現(xiàn)方式完全一樣,只是這里為所有顯示設(shè)備又定義了一個(gè)統(tǒng)一的接口,這個(gè)接口里定義了一個(gè)display()方法,也就是說(shuō)未來(lái)所有實(shí)現(xiàn)Observer和DisplayElement接口的對(duì)象應(yīng)該都可以作為氣象監(jiān)控系統(tǒng)的終端顯示設(shè)備,不同用戶可以在display()方法里任意自定義自己的顯示模式。因?yàn)闉榱朔乐够靵y,圖4中只畫(huà)了一個(gè)具體顯示設(shè)備對(duì)象,即CurrentConditionsDisplay,跟它同級(jí)別的還有StatisticsDisplay和ForcastDisplay,它們?cè)诮Y(jié)構(gòu)上完全相同。下面我們通過(guò)具體的代碼再進(jìn)一步理解一下基于觀察者模式的氣象監(jiān)控系統(tǒng)的實(shí)現(xiàn)。
ISubject:
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
關(guān)于這段代碼,似乎沒(méi)什么好說(shuō)的了,因?yàn)樯厦嬉呀?jīng)反復(fù)說(shuō)了很多啦。
IObserver:
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
這里我們給update()方法定義了三個(gè)對(duì)應(yīng)不同氣象數(shù)據(jù)的參數(shù)。
DisplayElement :
public interface DisplayElement {
public void display();
}
這個(gè)類(lèi)也是超級(jí)簡(jiǎn)單,沒(méi)什么可解釋的。
WeatherData:
public class WeatherData implements Subject {
private ArrayList observers;
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
this.observers = new ArrayList();
}
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temp, humidity, pressure);
}
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if (i >= 0) {
observers.remove(i);
}
}
public void measurementsChanged() {
notifyObservers();
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
這個(gè)類(lèi)是ISubject的具體實(shí)現(xiàn),內(nèi)部使用ArrayList來(lái)記錄所有注冊(cè)的觀察者,SetMeasurements() 方法是用來(lái)模擬前面提到的在天氣狀況改變的時(shí)候自動(dòng)觸發(fā)MeasurementsChanged()方法的機(jī)制。
CurrentConditionsDisplay:
public class CurrentConditionDisplay implements Observer, DisplayElement {
private Subject weatherData;
private float temp;
private float humidity;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("CurrentConditionDisplay temp = " + temp
+ " humidity = " + humidity);
}
}
這個(gè)類(lèi)是IObserver和IDisplayElement的具體實(shí)現(xiàn),代表顯示當(dāng)前天氣狀況的具體顯示設(shè)備對(duì)象,其內(nèi)部維護(hù)了一個(gè)ISubject類(lèi)型的變量,該變量在CurrentConditionsDisplay的構(gòu)造函數(shù)中被初始化,同時(shí)調(diào)用ISubject.registerObserver()方法,實(shí)現(xiàn)訂閱ISubject。
StatisticsDisplay和ForcastDisplay:
public class ForecastDisplay implements Observer, DisplayElement {
private Subject weatherData;
private float temp;
private float humidity;
private float pressure;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
@Override
public void display() {
System.out.println("ForecastDisplay temp = " + temp + " pressure = "
+ pressure);
}
}
StatisticsDisplay代碼類(lèi)似。
測(cè)試代碼:
public class WeatherTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(
weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMeasurements(80f, 90f, 30.4f);
weatherData.setMeasurements(100f, 99f, 40.0f);
}
}
..
使用JDK內(nèi)置的觀察者模式
若使用java內(nèi)置的觀察者模式,則 Subject 需要繼承 java.util.Observable 類(lèi)和, 觀察者實(shí)現(xiàn)java.util.Observer接口,具體代碼如下:
public class WeatherData extends Observable {
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
//此時(shí)已經(jīng)不需要我們自己定義存放觀察者的數(shù)據(jù)結(jié)構(gòu)了。
}
public void measurementsChanged() {
setChanged();
notifyObservers();// 調(diào)用notifyObservers方法之前,需先調(diào)用setChanged方法,來(lái)指示狀態(tài)已經(jīng)改變
/**
* Observable 類(lèi)中notifyObservers代碼如下: 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); } }
*
* Observable 類(lèi)中方法:
* protected synchronized void setChanged() { changed = true; }
**/
}
public void setMeasurements(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemp() {// 定義此方法,只因?yàn)橛^察者用它來(lái)“拉”數(shù)據(jù)
return temp;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
觀察者的定義如下:
public class CurrentConditionDisplay implements Observer, DisplayElement {
private Observable weatherData;
private float temp;
private float humidity;
public CurrentConditionDisplay(Observable weatherData) {
this.weatherData = weatherData;
weatherData.addObserver(this);
}
@Override
public void display() {
System.out.println("CurrentConditionDisplay temp = " + temp
+ " humidity = " + humidity);
}
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData) obs;
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity();
display();
}
}
}
其余代碼與上面自己定義接口的類(lèi)似。
應(yīng)用場(chǎng)景和優(yōu)缺點(diǎn)
上面已經(jīng)對(duì)觀察者模式做了比較詳細(xì)的介紹,還是那句話,人無(wú)完人,模式也不是萬(wàn)能的,我們要用好設(shè)計(jì)模式來(lái)解決我們的實(shí)際問(wèn)題,就必須熟知模式的應(yīng)用場(chǎng)景和優(yōu)缺點(diǎn):
觀察者模式的應(yīng)用場(chǎng)景:
1、 對(duì)一個(gè)對(duì)象狀態(tài)的更新,需要其他對(duì)象同步更新,而且其他對(duì)象的數(shù)量動(dòng)態(tài)可變。
2、 對(duì)象僅需要將自己的更新通知給其他對(duì)象而不需要知道其他對(duì)象的細(xì)節(jié)。
觀察者模式的優(yōu)點(diǎn):
1、 Subject和Observer之間是松偶合的,分別可以各自獨(dú)立改變。
2、 Subject在發(fā)送廣播通知的時(shí)候,無(wú)須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。
3、 遵守大部分GRASP原則和常用設(shè)計(jì)原則,高內(nèi)聚、低偶合。
觀察者模式的缺陷:
1、 松偶合導(dǎo)致代碼關(guān)系不明顯,有時(shí)可能難以理解。(廢話)
2、 如果一個(gè)Subject被大量Observer訂閱的話,在廣播通知的時(shí)候可能會(huì)有效率問(wèn)題。(畢竟只是簡(jiǎn)單的遍歷)
參考:http://www.cnblogs.com/justinw/archive/2007/05/02/734522.html#!comments
本文原創(chuàng)發(fā)布php中文網(wǎng),轉(zhuǎn)載請(qǐng)注明出處,感謝您的尊重!
總結(jié)
以上是生活随笔為你收集整理的观察者模式重复调用mysql问题,2、观察者模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: openssl1.1.0 支持php,o
- 下一篇: aov建立Java模拟,数据结构之---