一個demo徹底搞懂觀察者模式
一、介紹
觀察者模式也被稱為釋出-訂閱(Publish/Subscribe)模式,它屬於行為型模式的一種。觀察者模式定義了一種一對多的依賴關係,一個主題物件可被多個觀察者物件同時監聽。當這個主題物件狀態變化時,會通知所有觀察者物件並作出相應處理邏輯。
二、UML圖
三、入門案例分析 ~ ~ (無使用觀察者模式)
上面的內容比較抽象,這裡用一個案例加深對觀察者模式的理解!
需求:
- Internet氣象站專案:
提供溫度、氣壓和溼度的介面
測量資料更新時需時時通知給第三方 (通知)
需要設計開放型API,便於其他第三方公司也能接入氣象站獲取資料(訂閱)
- WeatherData類
- 通常的設計方案
1) 定義一個第三方公司的模板類
package com.dgut.edu.cn; /** * 第三方公司的模板類 */ public class CurrentCondition { // 環境溫度 private float temperature; // 環境氣壓 private float pressure; // 環境溼度 private float humidity; /** *更新資訊 * @param temperature * @param pressure * @param humidity */ public void update(float temperature,float pressure,float humidity){ this.temperature = temperature; this.pressure = pressure; this.humidity = humidity; display(); } /** * 展示資料使用 —— 每個公司的模板不一樣 */ public void display(){ System.out.println("***** the day temperature is " + temperature + "******"); System.out.println("***** the day pressure is " + pressure + "******"); System.out.println("***** the day humidity is " + humidity + "******"); } }
2) 定義氣象公司的介面類
package com.dgut.edu.cn; public class WeatherData { // 環境溫度 private float temperature; // 環境氣壓 private float pressure; // 環境溼度 private float humidity; public WeatherData() { } public float getTemperature() { return temperature; } public void setTemperature(float temperature) { this.temperature = temperature; } public float getPressure() { return pressure; } public void setPressure(float pressure) { this.pressure = pressure; } public float getHumidity() { return humidity; } public void setHumidity(float humidity) { this.humidity = humidity; } // 溫度、溼度、氣壓發生改變時,觸發函式 public void dataChange(){ } }
3) 因為氣象站的介面中,我們需要去呼叫第三方介面更新資料方法,故引入該第三方公司的實現類,並且在構造方法中進行例項化
在WeatherData中,加入以下程式碼:
// 別人的介面
private CurrentCondition currentCondition;
public WeatherData(CurrentCondition currentCondition) {
this.currentCondition = currentCondition;
}
4) 在氣象站介面中,寫一個跟新資料的方法,模擬氣象站自動更新資料
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
5) 在dataChange方法中去通知第三方介面更新資料
// 溫度、溼度、氣壓發生改變時,觸發函式
public void dataChange(){
currentCondition.update(temperature,pressure,humidity);
}
6) 寫測試案例如下:
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
WeatherData weatherData = new WeatherData(currentCondition);
weatherData.setData(20,50,100);
}
}
7) 執行結果:
***** the day temperature is 20.0******
***** the day pressure is 50.0******
***** the day humidity is 100.0******
- 分析上面存在的問題
1)其他第三方公司接入氣象站獲取資料的問題。
2)無法在執行時動態的新增第三方
【解釋:】在上述設計中,在Weather中引入currentCondition的成員變數,那如果有新的第三方模板接入,我們又得建立一個新第三方模板變數,手動去新增更新資料的程式碼。這樣子就很麻煩,在擴充套件性方面很弱。
- 新的一個解決方案:
根據這種訂閱/通知的設計思路,在Weather中提供一個註冊服務、移除服務、更改服務的方法,在第三方模板中,定義一個更新資料的介面Observer,在模板類去實現這個介面。而在Weather中,不在存入具體的子類,而是存一個Observer即可,這樣就可以完成在不改變WeatherData介面中,動態去新增多個訂閱者。
基於以上分析
Subject:登記註冊、移除和通知
Observer:接收輸入
【總結】到這裡就基本能理解觀察者模式的UML圖,提供註冊、移除、更改的介面,以及nodify展示資料的介面
- 入門案例進一步分析 ~ ~ (觀察者模式)
用觀察者模式設計重新設計的方案
1、寫observer介面
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
2、寫Subject介面
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObserver();
}
3、第三方模板類繼承該Observer介面
public class CurrentCondition implements Observer {
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
/**
*更新資訊
* @param temperature
* @param pressure
* @param humidity
*/
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("***** the day temperature is " + temperature + "******");
System.out.println("***** the day pressure is " + pressure + "******");
System.out.println("***** the day humidity is " + humidity + "******");
}
}
public class ForcastCondition implements Observer{
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
/**
*更新資訊
* @param temperature
* @param pressure
* @param humidity
*/
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("***** Tomorrow temperature is " + temperature + "******");
System.out.println("***** Tomorrow pressure is " + pressure + "******");
System.out.println("***** Tomorrow humidity is " + humidity + "******");
}
}
4、Weather類繼承該Subject介面
package cn;
import java.util.ArrayList;
public class WeatherDataSt implements Subject{
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
private ArrayList<Observer> observerArrayList = new ArrayList<Observer>();
public WeatherDataSt() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
// 溫度、溼度、氣壓發生改變時,觸發函式
public void dataChange(){
notifyObserver();
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
@Override
public void registerObserver(Observer observer) {
observerArrayList.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observerArrayList.contains(observer))
observerArrayList.remove(observer);
}
@Override
public void notifyObserver() {
for (int i = 0 ; i < observerArrayList.size(); i++){
observerArrayList.get(i).update(temperature,pressure,humidity);
}
}
}
5、寫測試案例如下
package cn;
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
ForcastCondition forcastCondition = new ForcastCondition();
WeatherDataSt weatherData = new WeatherDataSt(currentCondition);
weatherData.registerObserver(currentCondition);
weatherData.registerObserver(forcastCondition);
weatherData.setData(10,480,10);
System.out.println("========================================");
weatherData.removeObserver(currentCondition);
weatherData.setData(20,60,700);
}
}
6、執行結果
***** the day temperature is 10.0******
***** the day pressure is 480.0******
***** the day humidity is 10.0******
***** Tomorrow temperature is 10.0******
***** Tomorrow pressure is 480.0******
***** Tomorrow humidity is 10.0******
========================================
***** Tomorrow temperature is 20.0******
***** Tomorrow pressure is 60.0******
***** Tomorrow humidity is 700.0******
入門案例最終分析 ~ ~ (觀察者模式)— Java自帶類至此,我們已經實現了服務的動態訂閱和移除。
1、Java內建的觀察者
Observable
Observer
2、用Java內建觀察者重新設計該專案
3、內建觀察者的注意點
Observable是類而不是介面
- 程式碼例項如下:
1、模板類實現OBserver介面
package cn;
import java.util.Observable;
import java.util.Observer;
public class CurrentCondition implements Observer{
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
@Override
public void update(Observable o, Object arg) {
this.temperature = ((WeatherData.Data)(arg)).getTemperature();
this.pressure = ((WeatherData.Data)(arg)).getPressure();
this.humidity = ((WeatherData.Data)(arg)).getHumidity();
display();
}
public void display(){
System.out.println("***** the day temperature is " + temperature + "******");
System.out.println("***** the day pressure is " + pressure + "******");
System.out.println("***** the day humidity is " + humidity + "******");
}
}
package cn;
import java.util.Observable;
import java.util.Observer;
public class ForcastCondition implements Observer {
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
@Override
public void update(Observable o, Object arg) {
this.temperature = ((WeatherData.Data)(arg)).getTemperature();
this.pressure = ((WeatherData.Data)(arg)).getPressure();
this.humidity = ((WeatherData.Data)(arg)).getHumidity();
display();
}
public void display(){
System.out.println("***** Tomorrow temperature is " + temperature + "******");
System.out.println("***** Tomorrow pressure is " + pressure + "******");
System.out.println("***** Tomorrow humidity is " + humidity + "******");
}
}
2、Weacher繼承OBserverable介面
package cn;
import java.util.Observable;
public class WeatherData extends Observable{
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
public WeatherData() {
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
// 溫度、溼度、氣壓發生改變時,觸發函式
public void dataChange(){
this.setChanged();
this.notifyObservers(new Data(temperature,pressure,humidity));
}
public void setData(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
class Data{
// 環境溫度
private float temperature;
// 環境氣壓
private float pressure;
// 環境溼度
private float humidity;
public Data(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
}
public float getTemperature() {
return temperature;
}
public void setTemperature(float temperature) {
this.temperature = temperature;
}
public float getPressure() {
return pressure;
}
public void setPressure(float pressure) {
this.pressure = pressure;
}
public float getHumidity() {
return humidity;
}
public void setHumidity(float humidity) {
this.humidity = humidity;
}
@Override
public String toString() {
return "Data{" +
"temperature=" + temperature +
", pressure=" + pressure +
", humidity=" + humidity +
'}';
}
}
}
3、測試程式碼如下:
package cn;
public class Application {
public static void main(String[] args) {
CurrentCondition currentCondition = new CurrentCondition();
ForcastCondition forcastCondition = new ForcastCondition();
WeatherData weatherData = new WeatherData();
weatherData.addObserver(currentCondition);
weatherData.addObserver(forcastCondition);
weatherData.setData(111,222,333);
weatherData.deleteObserver(currentCondition);
weatherData.setData(1,2,3);
}
}
4、測試結果如下:
***** Tomorrow temperature is 111.0******
***** Tomorrow pressure is 222.0******
***** Tomorrow humidity is 333.0******
***** the day temperature is 111.0******
***** the day pressure is 222.0******
***** the day humidity is 333.0******
***** Tomorrow temperature is 1.0******
***** Tomorrow pressure is 2.0******
***** Tomorrow humidity is 3.0******