观察者模式重复调用mysql问题,2、观察者模式
整理: 氣象站的故事 現在我們要為一家氣象站開發一套氣象監控系統,按照客戶的要求,這個監控系統必須可以實時跟蹤當前的天氣狀況(溫度、濕度、大氣壓力),并且可以在三種不同設備上顯示出來(當前天氣狀況、天氣統計、天氣預測)。客戶還希望這個系統可以對外
整理:
氣象站的故事
現在我們要為一家氣象站開發一套氣象監控系統,按照客戶的要求,這個監控系統必須可以實時跟蹤當前的天氣狀況(溫度、濕度、大氣壓力),并且可以在三種不同設備上顯示出來(當前天氣狀況、天氣統計、天氣預測)。客戶還希望這個系統可以對外提供一個API接口,以便任何開發者都可以開發自己的顯示設備,然后無縫掛接到系統中,系統可以統一更新所有顯示設備的數據。客戶還會提供一個可以訪問氣象站的硬件設備的組件,如下圖所示:
它提供了三個方法(get開頭),可以分別取得實時的溫度、濕度和大氣壓力,還有一個MeasurementsChanged()方法,當任何天氣狀況發生變化的時候,這個方法都會自動被觸發,當前這個方法只是一個空函數,擴展的代碼還需要我們自己去擴充。至于WeatherData是如何取得天氣狀況的,還有MeasurementsChanged()方法是如何被自動觸發的這些事情都不需要我們去考慮,我們只管考慮如果做好跟顯示設備有關的事情就好了。
OK!讓我們來考慮一下這個系統的實現,先重新理一下思路:
1. 客戶提供了獲取實時的天氣狀況的方法。
2. MeasurementsChanged()方法會在天氣狀況變化時被自動調用。
3. 系統要實現三種顯示模式,分別顯示天氣狀況、天氣統計和天氣預測,而且這些顯示的信息必須跟當前最新的天氣狀況實時同步。
4. 系統還必須支持在顯示方式上的擴展性,而且使用者可以任意添加和移除不同的顯示模式。
基于上面這些信息,我們大概都會想到可以象下面這樣來實現這個系統:
//偽代碼
public class WeatherData
{
//實例化顯示設備(省略)
public void MeasurementsChanged()
{
float temp = getTemperature(); //取得溫度
float humidity = getHumidity(); //取得濕度
float pressure = getPressure(); //取得氣壓
currentConditionsDisplay.update(temp, humidity, pressure); //同步顯示當前天氣狀況
statisticsDisplay.update(temp, humidity, pressure); //同步顯示天氣統計信息
forecastDisplay.update(temp, humidity, pressure); //同步顯示天氣預報信息
}
}
因為客戶已經給我們提供了實時的數據,還提供了數據更新時候的觸發機制,那么我們要做的就是把最新的數據提供給不同的顯示設備就OK了,上面的代碼好象已經可以基本解決問題啦。哈哈!
面向接口編程,而不要面向實現編程。”的原則,這樣實現會帶來的問題是系統無法滿足在不修改代碼的情況下動態添加或移除不同的顯示設備。換句話說,顯示設備相關的部分是系統中最不穩定的部分,應該將其單獨隔離開,也就是前面學過的另一個原則:“找到系統中變化的部分,將變化的部分同其它穩定的部分隔開。”那么我們到底該怎么辦呢?呵呵,既然這篇文章是講觀察者模式的,當然要用它來結束戰斗!下面我們先來認識一下觀察者模式~
這就是觀察者模式
我們還是先看一下官方的定義:
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. (觀察者模式定義了對象間的一種一對多依賴關系,使得每當一個對象改變狀態,則所有依賴于它的對象都會得到通知并被自動更新)
咋樣?這是超級經典的標準定義,如假抱換的!不懂?那再看看下面的類圖吧~
Subject(被觀察的對象接口)
l 規定ConcreteSubject的統一接口;
l 每個Subject可以有多個Observer;
ConcreteSubject(具體被觀察對象)
l 維護對所有具體觀察者的引用的列表;
l 狀態發生變化時會發送通知給所有注冊的觀察者。
Observer(觀察者接口)
l 規定ConcreteObserver的統一接口;
l 定義了一個update()方法,在被觀察對象狀態改變時會被調用。
ConcreteObserver(具體觀察者)
l 維護一個對ConcreteSubject的引用;
l 特定狀態與ConcreteSubject同步;
l 實現Observer接口,通過update()方法接收ConcreteSubject的通知。
怎么樣,現在總該有點感覺了吧?下面還有一個順序圖,再體會體會~
呵呵!還沒想明白,為什么官方的東西總是看不懂,看來是沒當官的命啦!其實觀察者模式十分簡單,現實生活中的例子更是隨處可見,就比如看電視:某個觀眾就是一個標準的ConcreteObserver(具體觀察者,都符合統一的Observer接口,即都要通過電視收看節目的觀眾),電視節目就是Subject(被觀察對象接口,這里體現為無線電視信號)了,不同的頻道的節目是不同的ConcreteSubject(不同頻道有不同的節目),觀眾可以自由決定看電視(registerObserver)或不看電視(removeObserver),而電視節目的變化也會在自動更新(notifyObservers)所有觀眾的收看內容。怎么樣?這回明白了吧!
另外觀察者模式也叫發布-訂閱模式(Publishers + Subscribers = Observer Pattern),跟看電視一樣,訂閱報紙也是一個很直觀的例子,有人發布(Publish = Subject)報紙,有人訂閱(Subscribe = Observer)報紙,訂閱的人可以定期收到最新發布的報紙,訂閱人也可以隨時退訂。
現在大家應該對觀察者模式基本都了解了,我們來用這個模式來解決氣象站哪個問題。就氣象站問題的應用場景來說,WeatherData可以作為ConcreteSubject來看待,而不同的顯示設備則可以作為ConcreteObserver來看待,也就是說顯示設備觀察WeatherData對象,如果WeatherData對象有任何狀態變化,則立刻更新顯示設備的數據信息。這么說似乎很靠譜了,下面我們再來具體實現一下吧,先從整體結構開始,如下類圖:
跟前面說的實現方式完全一樣,只是這里為所有顯示設備又定義了一個統一的接口,這個接口里定義了一個display()方法,也就是說未來所有實現Observer和DisplayElement接口的對象應該都可以作為氣象監控系統的終端顯示設備,不同用戶可以在display()方法里任意自定義自己的顯示模式。因為為了防止混亂,圖4中只畫了一個具體顯示設備對象,即CurrentConditionsDisplay,跟它同級別的還有StatisticsDisplay和ForcastDisplay,它們在結構上完全相同。下面我們通過具體的代碼再進一步理解一下基于觀察者模式的氣象監控系統的實現。
ISubject:
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
關于這段代碼,似乎沒什么好說的了,因為上面已經反復說了很多啦。
IObserver:
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
這里我們給update()方法定義了三個對應不同氣象數據的參數。
DisplayElement :
public interface DisplayElement {
public void display();
}
這個類也是超級簡單,沒什么可解釋的。
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();
}
}
這個類是ISubject的具體實現,內部使用ArrayList來記錄所有注冊的觀察者,SetMeasurements() 方法是用來模擬前面提到的在天氣狀況改變的時候自動觸發MeasurementsChanged()方法的機制。
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);
}
}
這個類是IObserver和IDisplayElement的具體實現,代表顯示當前天氣狀況的具體顯示設備對象,其內部維護了一個ISubject類型的變量,該變量在CurrentConditionsDisplay的構造函數中被初始化,同時調用ISubject.registerObserver()方法,實現訂閱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代碼類似。
測試代碼:
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內置的觀察者模式
若使用java內置的觀察者模式,則 Subject 需要繼承 java.util.Observable 類和, 觀察者實現java.util.Observer接口,具體代碼如下:
public class WeatherData extends Observable {
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
//此時已經不需要我們自己定義存放觀察者的數據結構了。
}
public void measurementsChanged() {
setChanged();
notifyObservers();// 調用notifyObservers方法之前,需先調用setChanged方法,來指示狀態已經改變
/**
* Observable 類中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 類中方法:
* 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() {// 定義此方法,只因為觀察者用它來“拉”數據
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();
}
}
}
其余代碼與上面自己定義接口的類似。
應用場景和優缺點
上面已經對觀察者模式做了比較詳細的介紹,還是那句話,人無完人,模式也不是萬能的,我們要用好設計模式來解決我們的實際問題,就必須熟知模式的應用場景和優缺點:
觀察者模式的應用場景:
1、 對一個對象狀態的更新,需要其他對象同步更新,而且其他對象的數量動態可變。
2、 對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細節。
觀察者模式的優點:
1、 Subject和Observer之間是松偶合的,分別可以各自獨立改變。
2、 Subject在發送廣播通知的時候,無須指定具體的Observer,Observer可以自己決定是否要訂閱Subject的通知。
3、 遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。
觀察者模式的缺陷:
1、 松偶合導致代碼關系不明顯,有時可能難以理解。(廢話)
2、 如果一個Subject被大量Observer訂閱的話,在廣播通知的時候可能會有效率問題。(畢竟只是簡單的遍歷)
參考:http://www.cnblogs.com/justinw/archive/2007/05/02/734522.html#!comments
本文原創發布php中文網,轉載請注明出處,感謝您的尊重!
總結
以上是生活随笔為你收集整理的观察者模式重复调用mysql问题,2、观察者模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: openssl1.1.0 支持php,o
- 下一篇: aov建立Java模拟,数据结构之---