01.觀察者模式
1计贰、認(rèn)識觀察者模式:
我們看看報(bào)紙和雜志的訂閱是怎么回事:
(1)報(bào)社的業(yè)務(wù)就是出版報(bào)紙筷笨。
(2)向某家報(bào)社訂閱報(bào)紙拐袜,只要他們有新報(bào)紙出版冯凹,就會(huì)給你送來谎亩。只要你是他們的訂戶,你就會(huì)一直收到新報(bào)紙宇姚。
(3) 當(dāng)你不想再看報(bào)紙的時(shí)候团驱,取消訂閱,他們就不會(huì)再送新報(bào)紙來空凸。
(4)只要報(bào)社還在運(yùn)營嚎花,就會(huì)一直有人(或單位)向他們訂閱報(bào)紙或者取消訂閱報(bào)紙。
2呀洲、定義:
觀察者模式定義了對象之間的一對多依賴紊选,這樣一來,當(dāng)一個(gè)對象改變狀態(tài)時(shí)道逗,它的所有依賴者都會(huì)收到通知并自動(dòng)更新兵罢。
主題和觀察者定義了一對多的關(guān)系。觀察者依賴于此主題滓窍,只要主題狀態(tài)一有變化卖词,觀察者就會(huì)被通知。根據(jù)通知的風(fēng)格,觀察者可能因此新值而更新此蜈。
實(shí)現(xiàn)觀察者模式的方法不止一種即横,包含 Subject 和 Observer 接口的類最為常見。
3裆赵、設(shè)計(jì)原則:
為了交互對象之間的松耦合設(shè)計(jì)而努力东囚。
松耦合的設(shè)計(jì)之所以能讓我們建立有彈性的OO系統(tǒng),能夠應(yīng)對變化战授,是因?yàn)閷ο笾g的互相依賴降到最低页藻。
因?yàn)槿魏螘r(shí)候我們都可以添加新的觀察者,因?yàn)橹黝}唯一依賴的東西是一個(gè)實(shí)現(xiàn)Observer接口的對象列表植兰。
4份帐、舉例(氣象站項(xiàng)目):
4.1 需求:
(1)該氣象站必須建立在專利申請的 WeatherData 對象上,由 WeatherData 對象負(fù)責(zé)追蹤目前的天氣狀況(溫度楣导、濕度弥鹦、氣壓)。
(2)建立一個(gè)應(yīng)用爷辙,有三種布告板(后面可能會(huì)有更多)彬坏,分別顯示目前的狀態(tài)、氣象統(tǒng)計(jì)和簡單的預(yù)報(bào)(三種不同形式的展示)膝晾。當(dāng)WeatherData 改變時(shí)栓始,三種布告板必須實(shí)時(shí)更新。
4.2 設(shè)計(jì):
4.3 代碼實(shí)現(xiàn):
(1)觀察者接口
/**
* 觀察者接口
*
* 所有觀察者都要實(shí)現(xiàn)此接口
*/
public interface Observer {
/**
* 當(dāng)氣象觀察值改變時(shí)血当,主題會(huì)把這些狀態(tài)值傳給觀察者
* @param temp
* @param humidity
* @param pressure
*/
public void update(float temp, float humidity, float pressure);
}
(2)主題接口
/**
* 主題接口
*
* 所有主題都要實(shí)現(xiàn)此接口
*/
public interface Subject {
/**
* 觀察者注冊接口
* @param o
*/
public void registerObserver(Observer o);
/**
* 觀察者刪除接口
* @param o
*/
public void removeObserver(Observer o);
/**
* 當(dāng)主題狀態(tài)改變時(shí)幻赚,通過這個(gè)方法告知所有的觀察者
*/
public void notifyObservers();
}
(3)布告板接口
/**
* 布告板接口
*
* 所有布告板要實(shí)現(xiàn)此接口
*/
public interface DisplatElement {
/**
* 當(dāng)布告板需要顯示時(shí),調(diào)用此方法
*/
public void display();
}
(4)主題實(shí)現(xiàn)類 - 天氣數(shù)據(jù)
/**
* 天氣數(shù)據(jù)
*
* 主題實(shí)現(xiàn)類
*/
public class WeatherData implements Subject {
/**
* 記錄所有觀察者
*/
private ArrayList observers;
/**
* 溫度
*/
private float temperature;
/**
* 濕度
*/
private float humidity;
/**
* 氣壓
*/
private float pressure;
public WeatherData(ArrayList observers) {
this.observers = observers;
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
/**
* 當(dāng)檢測到觀察值發(fā)生改變臊旭,并且達(dá)到要通知的條件時(shí)落恼,通知所有觀察者
*/
public void measurementsChanged(){
//TODO 檢測是否達(dá)到通知條件,比如必須是溫度等條件達(dá)到0.5的浮動(dòng)再通知觀察者
notifyObservers();
}
/**
* 當(dāng)傳感器返回測量的數(shù)據(jù)离熏,會(huì)調(diào)用此方法
* @param temperature
* @param humidity
* @param pressure
*/
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
(5)三個(gè)布告板實(shí)現(xiàn)類佳谦,只有現(xiàn)實(shí)的屬性不同。
public class CurrentConditionsDisplay implements Observer, DisplatElement {
/**
* 溫度
*/
private float temperature;
/**
* 濕度
*/
private float humidity;
/**
* 主題
*/
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("CurrentConditionsDisplay【temperature:" + temperature + ";humidity:" + humidity+"】");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
}
public class ForecastDisplay implements Observer, DisplatElement {
/**
* 濕度
*/
private float humidity;
/**
* 氣壓
*/
private float pressure;
/**
* 主題
*/
private Subject weatherData;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("ForecastDisplay【humidity:" + humidity + ";pressure:" + pressure+"】");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.pressure = pressure;
this.humidity = humidity;
display();
}
}
public class StatisticsDisplay implements Observer, DisplatElement {
/**
* 溫度
*/
private float temperature;
/**
* 氣壓
*/
private float pressure;
/**
* 主題
*/
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("StatisticsDisplay【temperature:" + temperature + ";pressure:" + pressure+"】");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.pressure = pressure;
display();
}
}
(6)測試類
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(new ArrayList<Observer>());
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
weatherData.setMeasurements(50,60,30f);
weatherData.setMeasurements(40,50,20f);
weatherData.setMeasurements(60,20,40f);
}
}