觀察者模式 - Head First設計模式學習(一)

有這麼一個需求。

  1. 氣象站會發布氣象監測數據,包括(溫度,溼度,氣壓)
  2. 需要開發一個應用,從氣象站取得數據,並更新應用的佈告板。
  3. 佈告板有三類,分別是顯示當前天氣情況,氣象統計數據,天氣預報。

氣象站提供的數據結構如下,很簡單,就是溫度,溼度,氣壓的獲取,以及氣象數據變化時觸發的方法。


public class WeatherData {
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

    //氣象數據發生改變的時候執行的方法
	public void measurementsChanged() {
		
	}
	
	
}

按照通常面向過程的編程方式,會是在 measurementChanged 方法當中添加布告板的顯示更新邏輯。

public class WeatherData {
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

	CurrentConditionDisplay currentConditionDisplay = new CurrentConditionDisplay();  //當前天氣情況顯示
	
	ForecastDisplay forecastDisplay = new ForecastDisplay();  //天氣預報顯示
	
	StatisticsDisplay statisticsDisplay = new StatisticsDisplay();	 //氣象統計顯示
	
	public void measurementsChanged() {
		currentConditionDisplay.update(temperature, humidity, pressure);
		forecastDisplay.update(temperature, humidity, pressure);
		statisticsDisplay.update(temperature, humidity, pressure);
	}
	
}

這樣的編程方式導致每次要添加布告板的實現都要來修改 WeatherData 的代碼。

引入觀察者模式去解決這個問題。
什麼是觀察者模式,可以通過報紙和雜誌的訂閱來解釋。

  1. 報社的任務就是出版報紙
  2. 向某家報社訂閱報紙,只要他們有新報紙出版,就會給你送來。只要你是他們的訂戶,你就會一直收到新報紙。
  3. 當你不想看報紙的時候,取消訂閱,他們就不會給你送新報紙來。
  4. 只要報社還在運營,就會有人向他們訂閱報紙或者曲線訂閱

觀察者模式將出版者稱爲 “主題(Subject)”,訂閱者稱爲“觀察者(Observer)”

同訂閱報紙的模式。

  1. 觀察者可以訂閱主題
  2. 主題的信息發生改變時,會通知觀察者,把數據傳遞給觀察者。
  3. 觀察者不想接受主題的信息的時候就可以取消主題。

觀察者模式定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴都會收到通知並且自動更新。

觀察者類圖如下:

在這裏插入圖片描述

觀察者模式提供了一種對象設計,讓主題和觀察者之間松耦合。當兩個對象之間松耦合,他們依然可以交互,但是不太清楚彼此的細節。

改變主題或者觀察者的其中一方,並不會影響另一方。因爲兩者時松耦合的,所以只要他們直接的接口仍被遵守,我們就可以自由的改變他們。

松耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,是因爲對象之間的互相依賴降到了最低。

根據觀察者模式,重寫氣象佈告板的程序。
類圖設計如下:
在這裏插入圖片描述

代碼程序如下:

接口的設計

//主題定義
public interface Subject {
	
	public void registerObserver(Observer o);
	
	public void removeObserver(Observer o);
	
	public void notifyObserver();
	
}

//觀察者接口定義
public interface Observer {

	public void update(float temp, float humidity, float pressure);
	
}

//佈告板接口定義
public interface DisplayElement {
	
	public void display();
	
}

氣象站的數據源程序

public class WeatherData implements Subject{
	
	
	private ArrayList<Observer> observers;
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

	public WeatherData() {
		observers = new ArrayList<Observer>();
	}
	

	@Override
	public void registerObserver(Observer o) {
		observers.add(o);
	}

	@Override
	public void removeObserver(Observer o) {
		observers.remove(o);
	}

	@Override
	public void notifyObserver() {
		float temp = this.getTemperature();
		float humidity = this.getHumidity();
		float pressure = this.getPressure();
		for (Observer o : observers) {
			o.update(temp, humidity, pressure);
		}
	}
	
	public void measurementsChanged() {
		this.notifyObserver();
	}
	
	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		this.measurementsChanged();
	}
}

佈告板的實現


public class CurrentConditionDisplay implements Observer, DisplayElement {
	
	private float temperature;
	
	private float humidity;
	
	private float pressure;
	
	private Subject subject;
	
	public CurrentConditionDisplay(Subject weatherData) {
		this.subject = weatherData;
		weatherData.registerObserver(this);
	}
	
	@Override
	public void update(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		this.display();
	}

	@Override
	public void display() {
		System.out.println("this is  currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
	}
	
}

測試程序


public class Main {
	
	public static void main(String[] args) {
		WeatherData s = new WeatherData();
		Observer o = new CurrentConditionDisplay(s);
		s.setMeasurements(1, 2, 3);
		s.setMeasurements(4, 5, 6);
		
	}
	
}


測試結果:
this is  currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is  currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0

由於觀察者模式的普遍,各類語言或者框架都提供了觀察者模式的實現。
例如 java 提供了 Observable 和 Observer 兩個超類和接口。分別對應於前文提及的主題和觀察者。

使用 Observable 和 Observer 實現佈告板程序

氣象站數據源程序,繼承於 Observable 超類

package observer.example3;

import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

public class WeatherData extends Observable{
	
	
	private float temperature; 
	
	private float humidity;
	
	private float pressure;

	public float getTemperature() {
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

	public WeatherData() {
		
	}
	

	public void measurementsChanged() {
		this.setChanged(); //標識主題改變
		this.notifyObservers(this); //通知所有的觀察者
	}
	
	public void setMeasurements(float temp, float humidity, float pressure) {
		this.temperature = temp;
		this.humidity = humidity;
		this.pressure = pressure;
		this.measurementsChanged();
	}
}

佈告板程序,實現 Observer 接口,必須實現 update 方法

package observer.example3;

import java.util.Observable;
import java.util.Observer;

public class CurrentConditionDisplay implements Observer, DisplayElement {
	
	private float temperature;
	
	private float humidity;
	
	private float pressure;
	
	private Observable subject;
	
	public CurrentConditionDisplay(Observable weatherData) {
		this.subject = weatherData;
		weatherData.addObserver(this);
	}
	
	@Override
	public void display() {
		System.out.println("this is  currentConditionDisplay. temp: "+this.temperature+", humidity: "+this.humidity+", pressure:"+this.pressure);
	}

	//響應主題的推送
	@Override
	public void update(Observable o, Object arg) {
		WeatherData w = (WeatherData)o;
		this.temperature = w.getTemperature();
		this.humidity = w.getHumidity();
		this.pressure = w.getPressure();
		this.display();
	}
	
}

測試程序

public class Main {
	
	public static void main(String[] args) {
		WeatherData s = new WeatherData();
		Observer o = new CurrentConditionDisplay(s);
		s.setMeasurements(1, 2, 3);
		s.setMeasurements(4, 5, 6);
		
	}
	
}


測試程序
this is  currentConditionDisplay. temp: 1.0, humidity: 2.0, pressure:3.0
this is  currentConditionDisplay. temp: 4.0, humidity: 5.0, pressure:6.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章