java設計模式系列-觀察者模式
定義:
觀察者模式:
觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。
觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態上發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。
特點:
優點:
1、觀察者模式在被觀察者和觀察者之間建立一個抽象的耦合。被觀察者角色所知道的只是一個具體觀察者列表,每一個具體觀察者都符合一個抽象觀察者的介面。被觀察者並不認識任何一個具體觀察者,它只知道它們都有一個共同的介面。
2、觀察者模式支援廣播通訊。被觀察者會像所有登記過的觀察者發出通知。
缺點:
1、如果一個被觀察者物件有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
2、如果在被觀察者之間有迴圈依賴的話,被觀察者會觸發它們之間進行迴圈呼叫,導致系統崩潰。在使用觀察者模式是要特別注意這一點。
3、如果對觀察者的通知是通過另外的執行緒進行非同步投遞的話,系統必須保證投遞是以自恰的方式進行的。
4、雖然觀察者模式可以隨時使觀察者知道所觀察的物件發生了變化,但是觀察者模式沒有相應的機制使觀察者知道所觀察的物件是怎麼發生變化的。
具體使用場景:
1、觀察者模式多用於實現訂閱功能的場景,例如微博的訂閱,當我們訂閱了某個人的微博賬號,當這個人釋出了新的訊息,就會通知我們。
2、Servlet中的Listener中,觀察者模式的另一種形態——事件驅動模型
UML圖理解:
觀察者模式所涉及的角色有:
● 抽象主題(Subject)角色:抽象主題角色把所有對觀察者物件的引用儲存在一個聚集(比如ArrayList物件)裡,每個主題都可以有任何數量的觀察者。抽象主題提供一個介面,可以增加和刪除觀察者物件,抽象主題角色又叫做抽象被觀察者(Observable)角色。
● 具體主題(ConcreteSubject)角色:將有關狀態存入具體觀察者物件;在具體主題的內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色又叫做具體被觀察者(Concrete Observable)角色。
● 抽象觀察者(Observer)角色:為所有的具體觀察者定義一個介面,在得到主題的通知時更新自己,這個介面叫做更新介面。
● 具體觀察者(ConcreteObserver)角色:儲存與主題的狀態自恰的狀態。具體觀察者角色實現抽象觀察者角色所要求的更新介面,以便使本身的狀態與主題的狀態 像協調。如果需要,具體觀察者角色可以保持一個指向具體主題物件的引用。
程式碼演示:
藉助氣象站來說明觀察者模式具體實現, WeatherData為一個氣象站提供的氣象資訊的實體類,當WeatherData中的資訊發生變化時,需要讓兩個顯示面板(CurrentConditionsDisplay和AnotherCondisDisplay)中相應的資訊發生改變。這裡我們就可以使用觀察者設計模式。
抽象主題Subject
package com.gcc.designmode.observe;
/**
* 抽象主題
* @author gcc
*
* 2018年1月15日
*/
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyAllObserver();
}
具體主題:
package com.gcc.designmode.observe;
import java.util.ArrayList;
import java.util.List;
/**
* 具體主題WeatherData
* @author gcc
*
* 2018年1月15日
*/
public class WeatherData implements Subject{
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<Observer>();
}
@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(observer);
}
}
@Override
public void notifyAllObserver() {
for(int i=0;i<observers.size();i++) {
Observer obs = observers.get(i);
obs.update(temperature, humidity, pressure);
}
}
public void measurementsChanged() {
notifyAllObserver();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public List<Observer> getObservers() {
return observers;
}
public void setObservers(List<Observer> observers) {
this.observers = observers;
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
}
抽象觀察者:
package com.gcc.designmode.observe;
/**
* 抽象觀察者
* @author gcc
*
* 2018年1月15日
*/
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
展示介面:
package com.gcc.designmode.observe;
public interface DisplayElement {
public void display();
}
具體觀察(顯示最小,平均和最大溫度觀測值):
package com.gcc.designmode.observe;
/**
* 具體觀察者(顯示最小、平均和最大的溫度觀測值)
* @author gcc
*
* 2018年1月15日
*/
public class StatisticsDisplay implements Observer,DisplayElement{
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum= 0.0f;
private int numReadings;//記錄觀測的次數以便計算平均溫度值
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
+ "/" + maxTemp + "/" + minTemp);
}
@Override
public void update(float temp, float humidity, float pressure) {
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
}
具體觀察者(顯示當前溫度和溼度):
package com.gcc.designmode.observe;
/**
* 具體觀察者(顯示當前的溫度、溼度)
* @author gcc
*
* 2018年1月15日
*/
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;//溫度
private float humidity;//溼度
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
測試類:
package com.gcc.designmode.observe;
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(67, 45, 20.4f);
}
}
作者:scgyus