key words: 觀察者模式 Observer模式
definition:
defines a one-to-many dependency between objects so that when one object changes state,all of its dependents are notified and updated automatically.
物理模型:
觀察者注冊
觀察者通知:

觀察者撤銷注冊

business: weather station
天氣預報,在預報的數據更新后自動通知到各類不同顯示類型的終端,前提是這些終端向預報中心注冊了預定服務(register the service),這一點類似于訂閱報紙,你'訂閱'了以后就可以呆在家等郵遞員給你送過來.
看一下類圖:


implement:
public?interface?Subject?{
????public?void?registerObserver(Observer?o);
????public?void?removeObserver(Observer?o);
????public?void?notifyObservers();
}
public?class?WeatherData?implements?Subject?{
????private?ArrayList?observers;
????private?float?temperature;
????private?float?humidity;
????private?float?pressure;
????
????public?WeatherData()?{
????????observers?=?new?ArrayList();
????}
????
????public?void?registerObserver(Observer?o)?{
????????observers.add(o);
????}
????
????public?void?removeObserver(Observer?o)?{
????????int?i?=?observers.indexOf(o);
????????if?(i?>=?0)?{
????????????observers.remove(i);
????????}
????}
????
????public?void?notifyObservers()?{
????????for?(int?i?=?0;?i?<?observers.size();?i++)?{
????????????Observer?observer?=?(Observer)observers.get(i);
????????????observer.update(temperature,?humidity,?pressure);
????????}
????}
????
????public?void?measurementsChanged()?{
????????notifyObservers();
????}
????
????public?void?setMeasurements(float?temperature,?float?humidity,?float?pressure)?{
????????this.temperature?=?temperature;
????????this.humidity?=?humidity;
????????this.pressure?=?pressure;
????????measurementsChanged();
????}
????
????//?other?WeatherData?methods?here
????
????public?float?getTemperature()?{
????????return?temperature;
????}
????
????public?float?getHumidity()?{
????????return?humidity;
????}
????
????public?float?getPressure()?{
????????return?pressure;
????}
}
public?interface?Observer?{
????public?void?update(float?temp,?float?humidity,?float?pressure);
}
public?interface?DisplayElement?{
????public?void?display();
}
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);
????}
????
????public?void?update(float?temperature,?float?humidity,?float?pressure)?{
????????this.temperature?=?temperature;
????????this.humidity?=?humidity;
????????display();
????}
????
????public?void?display()?{
????????System.out.println("Current?conditions:?"?+?temperature?
????????????+?"F?degrees?and?"?+?humidity?+?"%?humidity");
????}
}
public?class?WeatherStation?{
????public?static?void?main(String[]?args)?{
????????WeatherData?weatherData?=?new?WeatherData();
???
??? ??? //register to weatherData(subscribe to weatherData)
????????CurrentConditionsDisplay?currentDisplay?=new?CurrentConditionsDisplay(weatherData);
????????StatisticsDisplay?statisticsDisplay?=?new?StatisticsDisplay(weatherData);
????????ForecastDisplay?forecastDisplay?=?new?ForecastDisplay(weatherData);
????????weatherData.setMeasurements(80,?65,?30.4f);
????????weatherData.setMeasurements(82,?70,?29.2f);
????????weatherData.setMeasurements(78,?90,?29.2f);
????}
}
public?class?WeatherStationHeatIndex?{
????public?static?void?main(String[]?args)?{
????????WeatherData?weatherData?=?new?WeatherData();
????????CurrentConditionsDisplay?currentDisplay?=?new?CurrentConditionsDisplay(weatherData);
????????StatisticsDisplay?statisticsDisplay?=?new?StatisticsDisplay(weatherData);
????????ForecastDisplay?forecastDisplay?=?new?ForecastDisplay(weatherData);
????????HeatIndexDisplay?heatIndexDisplay?=?new?HeatIndexDisplay(weatherData);
????????weatherData.setMeasurements(80,?65,?30.4f);
????????weatherData.setMeasurements(82,?70,?29.2f);
????????weatherData.setMeasurements(78,?90,?29.2f);
????}
}
other places to use Observe-Pattern in JDK:
public?class?SwingObserverExample?{
????JFrame?frame;
????
????public?static?void?main(String[]?args)?{
????????SwingObserverExample?example?=?new?SwingObserverExample();
????????example.go();
????}
????
????public?void?go()?{
????????frame?=?new?JFrame();
????????JButton?button?=?new?JButton("Should?I?do?it?");
??? ??? //register to AngelListener and DevilListener
????????button.addActionListener(new?AngelListener());
????????button.addActionListener(new?DevilListener());
????????frame.getContentPane().add(BorderLayout.CENTER,?button);
????????//?Set?frame?properties?
????????frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
????????frame.getContentPane().add(BorderLayout.CENTER,?button);
????????frame.setSize(300,300);
????????frame.setVisible(true);
????}
????
????class?AngelListener?implements?ActionListener?{
????????public?void?actionPerformed(ActionEvent?event)?{
????????????System.out.println("Don't?do?it,?you?might?regret?it!");
????????}
????}
????class?DevilListener?implements?ActionListener?{
????????public?void?actionPerformed(ActionEvent?event)?{
????????????System.out.println("Come?on,?do?it!");
????????}
????}
}
implement it with java built-in Observe-Pattern
import?java.util.Observable;
import?java.util.Observer;
????
public?class?WeatherData?extends?Observable?{
????private?float?temperature;
????private?float?humidity;
????private?float?pressure;
????
????public?WeatherData()?{?}
????
????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;
????}
}
增加一個看到的通俗版的解說:
觀察者模式?Observer Pattern — 三國演義之超級間諜戰 — 美女貂蟬的故事說明:我也是初學者,希望大家能提出寶貴意見。另外轉載請注明作者左光和出處博客園,畢竟花費了很長時間才完成。
情節:
這一次講的故事情節很簡單,但是充滿了謀略和斗爭。大體意思就是三國初期,曹劉孫三家在徐州聯手消滅了呂布,但是自己也傷了元氣。而此時袁術得了傳國玉璽,在淮南稱帝,兵精將廣,圖謀不軌,對三家威脅都很大。于是曹劉孫三家在一起開了個會,決定派遣一名超級間諜打入到袁術身旁,監視他的一舉一動,這樣的話一旦袁術想干什么壞事,他們就可以立刻知道并做出相應的調整,知己知彼百戰百勝嘛。
計是好計,問題是派誰去當這個超級間諜呢?正當大家愁眉苦臉的時候,美女貂蟬自告奮勇,想當年董卓那么厲害都讓
我滅了,何況一個小小的袁術呢?大家一聽,說的有理,于是就把貂蟬獻給了袁術當了妃子。另外三家還各派出一名小奸細常住在淮南城內,他們的任務是當聯絡
員,貂蟬有什么情報總不能自己曹劉孫三家挨個跑著送吧?直接丟給各國聯絡員,然后讓他們通知各自的主公就 OK
了!而三家只要一接到各自奸細的通知,就會立即做出反應。
還有一個小插曲,袁術雖然收下了貂蟬,但是對她看管很嚴,大大方方地把情報送出去不太可能,逼不得以,貂蟬自己設計了一套密碼來傳遞情報,雖然加密
方法沒有公開,但是她想總有人可以破解了吧?沒錯,對諸葛亮來說就是小菜一碟,從此袁術穿什么內褲曹劉孫三家都知道得清清楚楚。
分析:
下面我們用 觀察者模式? 來分析一下上面這個故事
1、美女貂蟬:貂蟬在觀察者模式中叫做被觀察者(Subject),主要任務是獨立的管理后臺數據和業務邏輯,同時盡可能不受前臺客戶端界面變化的影響。當然,還要負責登記或者注銷各個觀察者。
在這個故事里,貂蟬僅僅維護了一個數據 ,就是情報 —? 私有變量 info ;另外還擁有一個業務邏輯,是用來加密 info 的方法 Reverse(string str) 。每次得到新的情報,她就會先加密,然后立刻找到在自己這登記過的聯絡員,讓這些聯絡員通知自己的主公應變。
情報一旦發送出去, 貂蟬的任務就算完成了,具體曹劉孫三家怎么應對,是打是降還是另有其他方法,那是三家自己的事情,和她貂蟬就沒什么關系了。
2、曹劉孫三家:曹劉孫三家在觀察者模式里叫做觀察者,主要任務就是從界面上用“各種方式”即時的反映出 被觀察者,所謂“各種方式”就是說用字符、圖形、聲音都可以表示同樣數據,外在表現不同而已,本質都是一些數據。所謂“即時”,就是說只要 被觀察者 發生變化, 觀察者 也會立刻跟著變化,用行話應該叫做刷新 Update吧。
在這個故事里,我們可以看到運行結果中,每次貂蟬有什么新的情報,三家都會在屏幕上顯示出來,雖然很簡單,但 他們確實是用各自不同的方式表示了同樣的數據 。