客戶需求
/**
* 需求:
*
* 氣象站提供最新的天氣預(yù)報(bào)信息,包括: 溫度、濕度、氣壓,這些信息一發(fā)生變化, 客戶端的信息需要同時(shí)更新 請用代碼實(shí)現(xiàn)模擬實(shí)現(xiàn)該功能
*
*
*/
程序設(shè)計(jì)
一個(gè)氣象站對應(yīng)著多個(gè)客戶端,氣象站的數(shù)據(jù)一發(fā)生變化,客戶端的數(shù)據(jù)也要隨著更新,這就形成了一種依賴關(guān)系,并且是一對多的關(guān)系,很顯然,我們可以用觀察者模式來完成此功能
基本概念
- 定義
定義了對象之間的一對多依賴,這樣一來,當(dāng)一個(gè)對象改變狀態(tài)時(shí),它的所有依賴者都會收到通知并自動(dòng)更新
-
角色
- 抽象被觀察者(或者叫主題)
它是一個(gè)接口或者抽象類,用來把所有觀察者對象的引用保存到一個(gè)集合中。定義三個(gè)方法:注冊觀察者、解除觀察者、通知觀察者;這三個(gè)方法就是被觀察者與觀察者通信的橋梁
- 抽象觀察者
為所有的具體的觀察者定義的一個(gè)接口,定義一個(gè)方法:更新;用于獲取被觀察者發(fā)生變化時(shí)更新自己
- 具體的被觀察者(或者叫主題)
在被觀察者內(nèi)部狀態(tài)發(fā)生變化,通知所有注冊過的的觀察者。通常實(shí)現(xiàn)或繼承抽象被觀察者
- 具體的觀察者
實(shí)現(xiàn)抽象觀察者接口,使自己與具體的被觀察者的狀態(tài)保持一致
UML圖
-
DemoOne
-
被觀察者
public interface Subject { public void registerObserver(Observer observer); public void unregisterObserver(Observer observer); public void notifyObservers(); }
-
觀察者
public interface Observer { public void update(float temp, float humidity, float pressure); }
-
具體被觀察者
public class WeatherDataSubject implements Subject { private float temperature; private float humidity; private float pressure; /** * 記錄所有的觀察者 */ private ArrayList<Observer> observers; public WeatherDataSubject() { observers = new ArrayList<>(); } @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void unregisterObserver(Observer observer) { int indexOf = observers.indexOf(observer); if (indexOf >= 0) { observers.remove(indexOf); } } @Override public void notifyObservers() { for (int i = 0; i < observers.size(); i++) { Observer observer = observers.get(i); observer.update(temperature, humidity, pressure); } } public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; notifyObservers(); } }
-
具體觀察者
public class CurrentConditionsDidsplay implements Observer { @Override public void update(float temp, float humidity, float pressure) { System.out.println("temperature=" + temp + "\t humidity=" + humidity + "\t pressure=" + pressure); } }
-
測試
public class ObserverPatternTest { public static void main(String[] args) { WeatherDataSubject subject = new WeatherDataSubject(); CurrentConditionsDidsplay condition = new CurrentConditionsDidsplay(); subject.registerObserver(condition); subject.setMeasurements(23.0f, 1.0f, 350.0f); } }
-
以上這種方式只是簡單的實(shí)現(xiàn)了被觀察者數(shù)據(jù)發(fā)生變化時(shí),主動(dòng)將數(shù)據(jù)送過來,是一種推的模式。它不會管觀察者到底需不需要全部的數(shù)據(jù)。但是,有的觀察者可能只需要一點(diǎn)點(diǎn)數(shù)據(jù),不想收到一堆數(shù)據(jù),那此時(shí)怎么辦呢?那被觀察者是否可以提供一個(gè)get方法讓觀察者自己去獲取數(shù)據(jù),僅通知數(shù)據(jù)有變化了?接下來我們看看Java內(nèi)置的觀察者模式
- UML圖
- DemoTwo
-
具體的被觀察者
public class WeatherDataObservable extends Observable { private float temperature; private float humidity; private float pressure; public void setMeasurements(float temperature, float humidity, float pressure) { this.temperature = temperature; this.humidity = humidity; this.pressure = pressure; //僅僅是用來通知觀察者我的狀態(tài)發(fā)生變化了 setChanged(); notifyObservers(); } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public float getPressure() { return pressure; } }
-
具體的觀察者
public class CurrentConditionsDidsplayObserver implements Observer { @Override public void update(Observable observable, Object obj) { if (observable instanceof WeatherDataObservable) { WeatherDataObservable data = (WeatherDataObservable) observable; float temperature = data.getTemperature(); float humidity = data.getHumidity(); float pressure = data.getPressure(); System.out.println("temperature=" + temperature + "\t humidity=" + humidity + "\t pressure=" + pressure); } } }
-
java內(nèi)置的觀察者模式是屬于拉數(shù)據(jù)模式,被觀察者狀態(tài)發(fā)生變化了,通過setChanged方法僅通知觀察者狀態(tài)發(fā)生了變化,拉不拉數(shù)據(jù)是觀察者的事了,與被觀察者無關(guān)了。但是這種設(shè)計(jì)方式也會有一定的限制:
(1)Observable是一個(gè)類,所以我們的子類需繼承它,若此時(shí)被觀察者需同時(shí)繼承另一個(gè)超類,就會陷入兩難了,畢竟Java不支持多重繼承,這限制了Observable的復(fù)用能力
(2)Observable中的setChanged方法被protected了,這意味著:除非你繼承Observable類,否則你無法創(chuàng)建Observable實(shí)例并組合到你自己的對象中來。“多用組合,少用繼承”
知識總結(jié)
這兩種模式的使用,取決于系統(tǒng)設(shè)計(jì)時(shí)的需要。如果觀察者比較復(fù)雜,并且觀察者進(jìn)行更新時(shí)必須得到一些具體變化的信息,則“推模式”比較合適。如果觀察者比較簡單,則“拉模式”就比較適合。