《2》觀察者模式

問題:



=======條件=======

一個氣象站:可以用來採集溫度、溼度、氣壓等信息

一個WheatherData類:可用來獲取氣象站的數據,並且提供了一些自己的方法

	public class WeatherData {
		private int temperature;
		private int humidity;
		private int pressure;
	
		public int getHumidity() {
			return humidity;
		}

		public int getPressure() {
			return pressure;
		}

		public int getTemperature() {
			return temperature;
		}
	
		//溫度、溼度、壓力改變時會調用這個
		void parameterChanged(){
		
		}
	}






=======要求=======

有一個佈告欄,可以實時更新氣象臺的數據,並且我們可以肆意的刪減佈告欄的類型和數量


























解決方案一:



====1》創建統一的Display佈告欄接口:

public abstract class Display {
	abstract void update(int tem,int hum,int pre);
		
	abstract void display();
}



====2》創建佈告欄子類NanjingDisplay

public class NanjingDisplay extends Display{
	private int temp;
	private int hum;
	private int pre;
	@Override
	void update(int tem, int hum, int pre) {
		this.hum = hum;
		this.pre = pre;
		this.temp = tem;
		display();
	}

	@Override
	void display() {
		System.out.println("溫度:"+temp+" 溼度: "+hum+" 氣壓: "+pre);
	}

}


====3》更新NanjingDisplay

public class WeatherData {
	private int temperature;
	private int humidity;
	private int pressure;
	
	public int getHumidity() {
		return humidity;
	}

	public int getPressure() {
		return pressure;
	}

	public int getTemperature() {
		return temperature;
	}
	
	//溫度、溼度、壓力改變時會調用這個
	void parameterChanged(){
		int temp = getTemperature();
		int hum = getHumidity();
		int pre = getPressure();
		
		nanjingDisplay.update(temp,hum,pre);
	}
}

這裏我們不得不考慮客戶的要求:多個不同種類的佈告欄


1.傳參:我們怎麼在WheatherData中獲得Display的引用

放在構造函數參數中傳入,Display數量龐大的話會很麻煩


2.WheatherData的封裝性考慮:

如果後續每多增加或者減少一個Diaplay類別的話,我們都得修改

parameterChanged()函數,這還只是一個onChanged函數,如果客戶後續

添加別的類別的數據的changed函數,可以想像到時後的代碼維護將會是非常恐

怖的工作量


方案一fail

















解決方案二:

1》changed()函數頻繁修改解決方案:

    根據我們之前策略模式中提到的封裝變化規則,將changed()函數中的變數提取

    成一個接口,很顯然這個接口叫Display,而我們要調用他們的Diaplay.update()方法

2》傳參數量解決方案:

     這很簡單創建一個Dispaly[]數組即可,我們可以藉助數組進行增加和刪減操作


以上這些解決方案就接近我們的觀察者模式了


3》現在讓我們加入對接口編程這個規則,把對Display數組的增刪操作封裝成一個接口,

    Observable,這個就是被監聽者了,將所有的希望接受氣象數據的Display抽象成Observer接口

4》在加入多用組合少用繼承的規則,在Observable中用Display[]數組將 Observer們組合到自己旗下;

     所謂組合,即形容此時在Display[]數組中這些 Observer對象之間的關係,他們不是通過繼承來達到的,而是組合








下面引入觀察者模式框架圖




下面引入代碼


三個接口:

public interface Observable {
	boolean registerObserver(Observer o);
	boolean unregisterObserver(Observer o);
	void notifyObservers();
}

public interface Observer {
	void update(int arg0,int arg1,int arg2);
}

public abstract class Display {		
	abstract void display();
}


Display子類:

public class NanjingDisplay extends Display implements Observer{
	private int temp;
	private int hum;
	private int pre;
	private Observable observable;
	
	public NanjingDisplay(Observable observable) {
		//這裏認爲要不要監聽氣象數據應該是各個Display自己應該
		//思考的事,故而將這個註冊行爲寫在NanjingDisplay類內
		observable.registerObserver(this);
		
		//這裏需要保留observable的引用,方便unregister
		this.observable = observable;
	}
	
	@Override
	public void update(int tem, int hum, int pre) {
		this.hum = hum;
		this.pre = pre;
		this.temp = tem;
		display();
	}

	@Override
	void display() {
		System.out.println("南京氣象更新=="+"溫度:"+temp+" 溼度: "+hum+" 氣壓: "+pre);
	}

}


WheatherData類:

public class WeatherData implements Observable{
	private int temperature;
	private int humidity;
	private int pressure;
	
	public int getHumidity() {
		return humidity;
	}

	public int getPressure() {
		return pressure;
	}

	public int getTemperature() {
		return temperature;
	}
	
	//溫度、溼度、壓力改變時會調用這個
	void parameterChanged(int arg0,int arg1,int arg2){
		temperature = arg0;
		humidity = arg1;
		pressure = arg2;
		notifyObservers();
	}

	
	ArrayList<Observer> observers = new ArrayList<>();
	@Override
	public boolean registerObserver(Observer o) {
		if (o ==null){
			return false;
		}
		observers.add(o);
		return true;
	}

	@Override
	public boolean unregisterObserver(Observer o) {
		int i = observers.indexOf(o);
		
		if (i < 0){
			return false;
		}else{
			observers.remove(i);
			return true;
		}
		
	}

	@Override
	public void notifyObservers() {
		if (observers.size() == 0){
			return;
		}
		
		for(Observer o: observers){
			o.update(temperature, humidity, pressure);
		}
	}
}


Test測試類:

public class Test {
	public static void main(String[] args) {
		WeatherData observable = new WeatherData();
		Display nanjingDisplay = new NanjingDisplay(observable);
		Display beijingDisplay = new BeijingDisplay(observable);
		
		observable.parameterChanged(1,2,3);
		observable.parameterChanged(4,5,6);
		observable.parameterChanged(7,8,9);
	}
}




測試結果:

南京氣象更新==溫度:1溼度:2氣壓:3

北京氣象更新==溫度:1溼度:2氣壓:3


南京氣象更新==溫度:4溼度:5氣壓:6

北京氣象更新==溫度:4溼度:5氣壓:6


南京氣象更新==溫度:7溼度:8氣壓:9

北京氣象更新==溫度:7溼度:8氣壓:9







注意:還要考慮一個問題,update3個參數傳遞

1》作爲update的參數直接傳,最爲省事,但是存在問題,不是每個Display的子類都需要完整的

    3個參數,可能是1個、2個、或者不需要,這樣就造成了”浪費“

2》從上面的例子我們可以看到在WeatherData中還”隱藏“了get方法,而且Display子類中也保存WeatherData的對象,so,可以通過WeatherData.get來獲取自己需要的參數也     可以不獲取,但是這樣,會給Display的子類帶來負擔


此處示情況而定
















下面給出觀察者模式的定義

在對象之間設置一對多的依賴,這樣一來,當對象的狀態改變的時候,依賴他的多個對象就可以收到通知,並自動更新





觀察者模式優點

將可觀察者(Observable)和觀察者解耦,WeatherData不用關心 NanjingDisplay具體是個幹什麼的,

準確的說WeatherData只知道自己接收了一Observer類型的,而NanjingDiaplay也只是根據自己需要,

向一個 Observable註冊了

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