1. 程式人生 > >Head First設計模式:(二)觀察者模式

Head First設計模式:(二)觀察者模式

通過具體實現一個氣象監測系統來理解觀察者模式

此係統的三個部分是氣象站(獲取實際氣象資料的物理裝置)、WeatherData物件(追蹤來自氣象站的資料,並更新佈告板)和佈告板(顯示目前天氣狀況給使用者看)。

具體來說該應用需要:利用WeatherDate物件從氣象站取得資料,並更新三個佈告板:目前狀況、氣象統計和天氣預報。

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

主題和觀察者定義了一對多的關係。觀察者依賴於此主題,只要主題狀態一有變化,觀察者就會被通知。根據通知的風格,觀察者可能因此新值而更新。

關於觀察者的一切,主題只知道觀察者實現了某個介面(Observer介面)。主題不需要知道觀察者的具體類是誰、做了些什麼或其他任何細節,將物件之間的相互依賴性降到最低。符合了

設計原則:

為了互動物件之間的鬆耦合設計和努力。

結合氣象站的需求和觀察者模式的定義,得到氣象站的設計圖如下:

從圖中可以看出,有三個介面需要建立:Subject、Observer、DisplayElement

Subject:

package com.lissdy;

public interface Subject {

	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObserver();
}

Observer:

package com.lissdy;

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

DisplayElement:

package com.lissdy;

public interface DisplayElement {
    public void display();
}

在WeatherData中實現Subject介面:

package com.lissdy;

import java.util.ArrayList;

public class WeatherData implements Subject {

	private ArrayList observers;
	private float temperature;
	private float pressure;
	private float humidity;

	public WeatherData() {
		observers = new ArrayList();  //加上一個ArrayList來記錄觀察者,此ArrayList是在構造器中產生的
	}

	public void registerObserver(Observer o) {
		observers.add(o);                    //有觀察者註冊時,將其加到ArrayList後面
	}

	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);       //觀察者取消註冊時,將其從ArrayList中刪除
		if (i >= 0) {
			observers.remove(i);
		}
	}

	public void notifyObserver() {
		for (int i = 0; i < observers.size(); i++) {            //通知每一位觀察者
			Observer observer = (Observer) observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}

	public void measurementsChanged() {
		notifyObserver();
	}

	public void setMeasurements(float temperature, float humidity,
			float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

}

建立當前天氣狀況和溫度統計的佈告板:

當前天氣狀況:

package com.lissdy;

public class CurrentDisplay implements Observer,DisplayElement{
   private float temperature;
   private float humidity;
   private Subject weatherDate;
   
   public CurrentDisplay(Subject weatherDate)
   {
	   this.weatherDate=weatherDate;
	   weatherDate.registerObserver(this);
   }
   public void update(float temperature,float humidity,float pressure)
   {
	   this.temperature=temperature;
	   this.humidity=humidity;
	   display();
   }
   public void display()
   {
	   System.out.println("目前狀況是溫度:"+temperature+"度     "+"溼度:"+humidity+"%");
   }
}

溫度統計:

package com.lissdy;

public class StatisticsDisplay implements Observer,DisplayElement{
   private float temperature;
   private Subject weatherDate;
   private float max=0;
   private float min=100;
   private float sum=0;
   private int i=0;
   public StatisticsDisplay(Subject weatherDate)
   {
	   this.weatherDate=weatherDate;
	   weatherDate.registerObserver(this);
   }
   public void update(float temperature,float humidity,float pressure)
   {
	   this.temperature=temperature;
	   i++;
	   sum=sum+temperature;
	   if(temperature>max)
	   {
		   max=temperature;
	   }
	   if(temperature<min)
	   {
		   min=temperature;
	   }
	   display();
   }
   public void display()
   {
	   System.out.println("平均溫度是:"+(sum/i)+"最高溫度是:"+max+"最低溫度是:"+min);
   }
}

建立一個測試程式:

package com.lissdy;

public class WeatherStation {
	
	public static void main(String[] args)
	{
	   WeatherData weatherData=new WeatherData();   //建立一個WeatherData物件
	   CurrentDisplay currentDisplay=new CurrentDisplay(weatherData);  //建立佈告板,並把WeatherData傳給它們
	   StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
	   
	   weatherData.setMeasurements(80, 65, 30.4f);  //模擬新的氣象測量
	   weatherData.setMeasurements(82, 70, 29.2f);
	   weatherData.setMeasurements(78, 90, 29.2f);
	}
}

執行結果:


以上採用自己構建觀察者模式的方法完成了氣象站系統。但是,JAVA API內有內建的觀察者模式

java .util包內包含了最基本的Observer介面和Observable類,這和之前自己構造的Observer介面和Subject介面很相似。

若使用java內建觀察者模式實現氣象站系統,其設計圖為:

利用內建的支援重做氣象站:
首先,把WeatherData改成使用java.util.Observable

package com.lissdy;

import java.util.Observable;

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

	public WeatherData() {
	} // 不需要再使用ArrayList來記錄觀察者了,API代勞

	public void measurementsChanged() {
		setChanged();
		notifyObservers();
	}

	public void setMeasurements(float temperature, float humidity,
			float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}

	public float getTemperature() // 讓觀察者自己"拉"走需要的資料
	{
		return temperature;
	}

	public float getHumidity() {
		return humidity;
	}

	public float getPressure() {
		return pressure;
	}

}

重做佈告板:

當前天氣:

package com.lissdy;

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

public class CurrentDisplay implements Observer, DisplayElement {
	Observable observable;
	private float temperature;
	private float humidity;

	public CurrentDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	public void update(Observable obs, Object arg) {
		if (obs instanceof WeatherData) {
			WeatherData weatherData = (WeatherData) obs;
			this.temperature = weatherData.getTemperature();
			this.humidity = weatherData.getHumidity();
			display();
		}
	}

	public void display() {
		System.out.println("目前狀況是溫度:" + temperature + "度     " + "溼度:"
				+ humidity + "%");
	}

}

氣溫統計:

package com.lissdy;

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

public class StatisticsDisplay implements Observer, DisplayElement {
	Observable observable;
	private float temperature;
	private float max = 0;
	private float min = 100;
	private float sum = 0;
	private int i = 0;

	public StatisticsDisplay(Observable observable) {
		this.observable = observable;
		observable.addObserver(this);
	}

	public void update(Observable obs, Object args) {
		if (obs instanceof WeatherData) {
			WeatherData weatherData = (WeatherData) obs;
			this.temperature = weatherData.getTemperature();
			i++;
			sum = sum + temperature;
			if (temperature > max) {
				max = temperature;
			}
			if (temperature < min) {
				min = temperature;
			}
			display();
		}
	}

	public void display() {
		System.out.println("平均溫度是:" + (sum / i) + "最高溫度是:" + max + "最低溫度是:"
				+ min);
	}
}

執行結果:

注意和之前得到的結果相同,但是佈告板的排列順序不同。這是由於自己實現的觀察者模式和JAVA API中的notifyObservers()方法實現方式不同造成的。

java.util.Observable是一個類而不是一個介面,違反了針對介面程式設計,而非針對實現程式設計的設計原則。

在JavaBeans和Swing中也都實現了觀察者模式,例如一個按鈕繫結兩個監聽器,按下按鈕時,兩個監聽器都被觸發。

相關推薦

Head First設計模式觀察模式

通過具體實現一個氣象監測系統來理解觀察者模式此係統的三個部分是氣象站(獲取實際氣象資料的物理裝置)、WeatherData物件(追蹤來自氣象站的資料,並更新佈告板)和佈告板(顯示目前天氣狀況給使用者看)。 具體來說該應用需要:利用WeatherDate物件從氣象站取得資料,

設計模式學習觀察模式C#

original pan 學習筆記 pri 接口 program date contain 兩個 《深入淺出設計模式》學習筆記第二章 需求: 開發一套氣象監測應用,如圖: 氣象站,目前有三種裝置,溫度、濕度和氣壓感應裝置。 WeatherData對象追蹤氣象站的數據,並更

Head First 設計模式觀察模式

觀察者模式 定義 首先看下觀察者模式的定義 觀察者模式定義了物件之間的一對多依賴,這樣一來,當一個物件改變狀態時,它的所有依賴者都會收到通知並自動更新 按我自己的理解翻譯就是: 觀察者模式就是一個主題和多個依賴者的關係,當主題發生變化

Head First設計模式裝飾模式

星巴茲咖啡準備更新訂單系統,以合乎他們的飲料供應需求。 他們原先的類設計為: 這樣的訂單系統沒有辦法考慮到咖啡調料的部分,把加入不同調料的咖啡看做不同的類會導致類爆炸(每個類的cost方法計算出咖啡加調料的價錢): 很明顯,這樣的系統難以維護,一旦牛奶的價錢上揚或新增一

設計模式—— 觀察模式

pda 發出 dex obs index observe target 獲取 委托事件 觀察者模式類似出版社與訂閱者的關系,訂閱者能夠收到出版社發出的消息,可以取消訂閱。出版社在觀察者模式中稱為主題(Subject)訂閱者稱為觀察者(Observer)。 主

一看就懂【來自英雄聯盟蓋倫的怒吼】與 Python 詳解設計模式觀察模式

觀察者模式概述 觀察者模式(有時又被稱為模型-檢視(View)模式、源-收聽者(Listener)模式或從屬者模式)是軟體設計模式的一種。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實現事件處理系統

Unity遊戲設計模式觀察模式Observer Pattern

        最近看遊戲設計模式,當看到觀察者模式時被搞得雲裡霧裡的,什麼觀察者,被觀察者,抽象觀察者,抽象被觀察者。聽著這些詞就覺得可怕,其實理解以後還是比較簡單的。         當我們玩遊戲時,經常會出現一些事件,而這個事件可能會影響到許多個模組時就可以用到觀察者

設計模式練習17——觀察模式

觀察者模式 一、題目: 某公司欲開發一套機房監控系統,如果機房達到一定指定溫度,感測器將作出反應,將訊號傳遞給響應裝置,如警示燈將閃爍,報警器將發出警報,安全逃生門將自動開啟、隔熱門將自動關閉,每一響應裝置的行為右專門的程式來控制,為支援將來引入新型別的響

設計模式觀察監聽者模式

blog virtual ack memory message efault share default IT 基於MAP線程安全的觀察者監聽者模式 1 #include<map> 2 #include<string> 3 #i

23種設計模式介紹---- 結構型模式

implement weight 代碼 介紹 定義 裝飾器模式 大量 技術分享 記憶 由於設計模式篇幅比較大,如果在一篇文章講完所有的設計模式的話不利於閱讀。於是我把它分為三篇文章 23種設計模式介紹(一)---- 創建型模式 23種設計模式介紹(二)---- 結構

設計模式與XML建造模式和單例模式(C++)

一、實驗目的及要求 1、掌握建立型模式的概念。 2、掌握工廠模式、抽象工廠模式、單例模式、建造者模式、原型模式的構造方式及使用情景。 二、實驗裝置(環境) 1、   軟體需求: Dev-Cpp5.4, Rational Rose / Microsoft Visio

Head First Python 讀書筆記

第四章 :函式與模組 定義函式:def 函式註釋文件 def icessun(): # 這也是註釋,下面是函式文件註釋,描述函式的用途 """this is function document as docstring"""

設計模式觀察模式

針對 ray 需求 als bool 模式 null rri 主動 觀察者模式 定義了對象之間的一對多的依賴,這樣一來,當一個對象狀態改變時,他的 多有依賴都會受到通知並自動更新。 本例為一個溫度基站,和三個終端。溫度基站為廣播類WeatherData,三個終端監聽者類分別

設計模式解密6 - 建造模式

簡單的 對比 ide nbsp blog art 是把 部分 shp 1、簡介 定義:將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。 英文:Builder 類型:創建類模式 2、原理及組成 引:類圖 四個要素 產品類:

設計模式18觀察模式

模式介紹 觀察者模式試圖允許物件在其內部狀態改變時通知觀察者。 這意味著單個物件需要知道觀察它的物件,並且當狀態發生變化的時候,需要能夠與那些觀察者通訊。此外,觀察者應該被自動通知。 示例 設想我們需要一個系統來建模本地蔬菜市場中蔬菜價格的波動。 我們需要允許餐館檢視價格,並在特定蔬菜的價格低於指定閾

PHP設計模式5—— 觀察模式

基本概念 整個觀察者模式,其實做的就是監聽事件、處理事件。 由此引出兩個角色,一個是觀察者Listener,另一個是事件處理者Handler。 例子 第一步,建立抽象事件類。 它的主要作用是提供兩個方法給具體事件類呼叫。 abstract clas

設計模式觀察模式

繼續設計模式的文章,今天給大家帶來觀察者模式。先來看看觀察者模式的定義:定義了物件之間的一對多的依賴,這樣一來,當一個物件改變時,它的所有的依賴者都會收到通知並自動更新。好了,對於定義的理解總是需要例項來解析的,如今的微信服務號相當火啊,下面就以微信服務號為背景,給大家介紹

設計模式——裝飾模式Decorator Pattern

    裝飾者模式:動態將責任附加到物件上,要拓展功能,提供了比繼承更有彈性的方案。 很多文章也是拿了書上的例子來講,同時寫到,有的調料裝飾者都必須實現 getDescription() 大家可以先考慮下,稍後我們會說。最後都是沒說,還有思考的

《JavaScript設計模式與開發實踐》模式11—— 中介模式

中介者模式的作用就是解除物件與物件之間的緊耦合關係。增加一箇中介者物件後,所有的 相關物件都通過中介者物件來通訊,而不是互相引用,所以當一個物件發生改變時,只需要通知 中介者物件即可。中介者使各物件之間耦合鬆散,而且可以獨立地改變它們之間的互動。中介者模式使網狀的多對多關係變成了相對簡單的一對多關係

Java設計模式4--觀察模式

今天看了一下觀察者模式,總體來說不難,關鍵是要知道原理和這個模式的用法下面是我自己個人一些理解。 1、定義 觀察者模式,首先得有觀察者,一個或者多個觀察者,它們都依賴於另一個物件,也就是說,這些觀察者都觀察著一個被觀察者。那麼,當被觀察者狀態發生改變