讓對象保持消息靈通 #01需求 一個WeatherData對象負責追蹤目前的天氣狀況(溫度,濕度,氣壓)。希望你們能建立一個應用,有三種佈告板,分別顯示目前的狀況、氣象統計及簡單的預報。當WeatherObject對象獲得最新的測量數據時,三種佈告板必須實時更新。而且,這是一個可以擴展的氣象站,We ...
讓對象保持消息靈通
01需求
一個WeatherData對象負責追蹤目前的天氣狀況(溫度,濕度,氣壓)。希望你們能建立一個應用,有三種佈告板,分別顯示目前的狀況、氣象統計及簡單的預報。當WeatherObject對象獲得最新的測量數據時,三種佈告板必須實時更新。而且,這是一個可以擴展的氣象站,Weather-O-Rama氣象站希望公佈一組API,好讓其他開發人員可以寫出自己的氣象佈告板,並插入此應用中,我們希望能提供這樣的API。
class WeatherData{
public int getTemperature(){
}
public int getHumidity(){
}
public int getPressure(){
}
public void measurementsChanged(){
//一旦氣象測量更新,此方法會被調用
}
}
實現1
public class WeatherData {
// 實例變數聲明
public void measurementsChanged() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// 這裡是其他WeatherData方法
}
缺點:
>針對實現編程
對於每個新的顯示,都得修改這份代碼
沒有辦法在運行時添加/移除顯示元素
沒有封裝改變的部分
02觀察者模式
觀察者模式:定義對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新;
有幾種不同的實現方法,大多數圍繞著包括主題和觀察者介面的類設計
類圖:
第四個設計原則:為了交互對象之間松耦合設計而努力。
>主題只知道觀察者實現了某個介面(也就是Observer介面)
任何時候我們都可以增加新的觀察者。也可以在任何時候刪除某些觀察者。
有新類型的觀察者出現時,主題的代碼不需要修改,
獨立復用主題和觀察者
改變主題/觀察者其中一方,不會影響另一方
實現2
設計氣象站
實現氣象站
1.構建主題subject和觀察者observer介面
//主題介面
public interface Subject{
//註冊觀察者
public void registerObserver(Observer o);
//刪除觀察者
public void removeObserver(Observer o);
//當主題狀態改變時,這個方法會被調用,以通知所有的觀察者
public void notifyObserver();
}
public interface Observer {
//當氣象觀測值改變時,主題會把這些狀態值當作方法的參數,傳送給觀察者
public void update(float temp,float humidity,float pressure);
}
public interface DisplayElement{
//當需要顯示時,調用此方法
public void display();
}
2.WeatherData類實現主題介面
public class WeatherData implements Subject{
private ArrayList<observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=new ArrayList<observer>();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);//一個觀察者註冊的時候,把它添加到list的末端
}
@Override
public void removeObserver(Observer o) {
int i=observers.indexOf(o);//獲得對象索引
if(i>=0){
observers.remove(i);
}
}
@Override
public void notifyObserver() {
for(Observer observer:observers){
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();
}
//WeatherData的其他方法
}
3.構造顯示元素
顯示類實現Observer介面,所以可以從WeatherData對象中獲取變化
public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
//構造器被傳入weatherData對象(Subject),用它來把 【顯示】 註冊為 【觀察者】
public CurrentConditionDisplay(WeatherData weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("current conditions:" + temperature + "F degrees and " + humidity + "% humidity");
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature=temp;
this.humidity=humidity;
display();
}
}
4.啟動氣象站
public class WeatherStation {
public static void main(String[] args) {
//創建對象
WeatherData weatherData = new WeatherData();
//創建顯示
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
//模擬氣象測量
weatherData.setMeasurements(80,65,30.4f);
}
}
缺點:
>每次更新信息都會全部通知到每個觀察者,而觀察者只想要有用的信息;
不能擴展更多的顯示【因為觀察者介面的update方法是固定參數】
實現3(改進)
我們可以讓Observer按需要從Subject拉取。當Subiect的數據變化,我們馬上通過調用update()傳送數據,推送新的溫度、濕度和氣壓值給Observer。
為了切換到拉取方式,我們需要對已有代碼做一些小小的修改
Subject的發送通知.....
修改WeatherData中的notifyObservers()方法,不帶參數地調用Observer中的update()方法:
public void notifyobservers() {
for(Observer observer:observers){
observer.update();
}
}
Observer的接收通知....
修改Observer介面,改變update()方法的簽名,這樣它就沒有參數了:
public interface Observer {
public void update();
}
修改每個具體Observer,改變其各自的update0方法的簽名,並使用WeatherData的getter方法從Subject獲取氣象數據。CurrentConditionsDisplay類的新代碼如下:
public void update() {
this.temperature = weatherData.getTemperature();
this,humidity = weatherData.getHumidity();
display();
}
總結
OO基礎:抽象
OO原則:
>封裝變化。
組合優於繼承
針對介面編程,而不是針對實現。
儘力達到交互對象之間的松耦合設計。【新增】
OO模式:
觀察者模式:定義對象之間的一對多依賴這樣,當一個對象改變狀態時,它的所有依賴者會被通知並自動更新。
要點
觀察者模式定義對象之間的一對多關係。
主題使用通用介面更新觀察者。
任何具體類型的觀察者都可參與該模式,只要它們實現察者介面。
觀察者是松耦合的,除了知道它們實現觀察者介面之外,主題對它們的其他事情不知情
使用該模式時,你可以從主題推或拉數據(拉被認為更“正確”)
Swing大量使用觀察者模式許多GUI框架也是這樣。
你也會在其他很多地方發現該模式,包括RxJava、JavaBeans和RMI,以及其他語言的框架,像Cocoa、Swift和JavaScript事件。
觀察者模式和出版/訂閱模式相關。出版/訂閱模式用於更複雜得多主題和/或多消息類型的情形。
觀察者模式是一個常用的模式,當我們學習模型-視圖-控制器時,還會看到它。