現在是大三寒假,在大二暑假時進入國內某互聯網上市公司做Android開發,半年多的學習和工作積累了很多,剛好現在下班後不用急的趕回去上課,就留在公司寫博客總結一下。《設計模式》這本經典的書是之前在公交車上讀的,現在計劃每隔一段時間總結一個專題,回顧一下。今天回顧一下第一個模式:觀察者模式。由於本人能力有限,這也是本人寫的第一篇博客,有錯誤的地方希望各位大神指點。
認識觀察者模式
舉一個生活中的例子,報社的業務是出版報紙,如果你向報社訂閱了報紙,那麼他們只要有新報紙出版,就會給你送來,如果你不想看了,可以取消訂閱,那麼他們就不會把報紙送來了。他們可以有很多個訂閱者,只要報社在運營,就會一直有人想他們訂閱或取消訂閱報紙。
出版者+訂閱者=觀察者模式
如果你瞭解了的訂閱是怎麼回事,那麼你就明白了觀察者模式是怎麼回事,在這裏“出版者”被稱爲“主題”(Subject),“訂閱者”被稱爲“觀察者”(Observer)。
觀察者模式
定義了對象之間的一對多依賴,當一個對象發生改變時,它所有的依賴者就會搜到通知,並自動更新。
模式中的角色
1.抽象主題角色(Subject):目標角色知道它的觀察者,可以有任意多個觀察者觀察同一個目標。並且提供註冊和刪除觀察者對象的接口。目標角色往往由抽象類或者接口來實現。
2. 抽象觀察者角色(Observer):爲那些在目標發生改變時需要獲得通知的對象定義一個更新接口。抽象觀察者角色主要由抽象類或者接口來實現。
3.具體主題角色(Concrete Subject):將有關狀態存入各個Concrete Observer對象。當它的狀態發生改變時, 向它的各個觀察者發出通知。
4.具體觀察者角色(Concrete Observer):存儲有關狀態,這些狀態應與目標的狀態保持一致。實現Observer的更新接口以使自身狀態與目標的狀態保持一致。在本角色內也可以維護一個指向Concrete Subject對象的引用。
實際工程項目中的應用
下面來結合一個實際工程項目:氣象監測站會將傳感器收集到的數據(溫度Temperature,溼度Humidity,氣壓Pressure)傳給WeatherData對象,利用該對象來實時更新顯示三個佈告板:目前天氣狀況(CurrentConditions),氣象統計(Statistics)和天氣預報(Forecast)。
觀察者模式定義了一對多的依賴關係,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並更新。那麼WeatherData類就是“一”,“多”就是使用天氣觀測的各種佈告板。我們把WeatherData對象當做“主題(Subject)",把佈告板當做觀察者,佈告板爲了取得信息,向WeatherData對象註冊。一旦WeatherData對象知道某個佈告板的存在,就會調用update方法,來對佈告板進行更新。update是在觀察者Observer接口中定義的。
類圖
實現代碼
主題Subject接口:利用該接口中的函數可以註冊,移除和通知觀察者
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
觀察者Observer接口:主題通過調用update方法來通知各個實現Observer接口的觀察者們。
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
具體主題角色(Concrete Subject)
</pre><pre name="code" class="java">public class WeatherData implements Subject{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=new ArrayList();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i=observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
@Override
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();
}
}
具體觀察者角色CurrentConditionDisplay
public class CurrentConditionsDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature=temp;
this.humidity=humidity;
this.pressure=pressure;
System.out.println("當前天氣狀態 :"+temperature+"度 , "+humidity+
"溼度 和 "+pressure+"帕");
}
}
具體觀察者角色StatisticsDisplay
public class StatisticsDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature=temp;
this.humidity=humidity;
this.pressure=pressure;
System.out.println("統計天氣顯示 :"+temperature+"度 , "+humidity+
"溼度 和 "+pressure+"帕");
}
}
具體觀察者角色ForecastDisplay
public class ForecastDisplay implements Observer{
private float temperature;
private float humidity;
private float pressure;
private Subject weatherData;
public ForecastDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature=temp;
this.humidity=humidity;
this.pressure=pressure;
System.out.println("預測天氣顯示 :"+temperature+"度 , "+humidity+
"溼度 和 "+pressure+"帕");
}
}
測試代碼
public class WeatherStation {
public static void main(String []args){
//首先建立一個主題WeatherData對象
WeatherData weatherData=new WeatherData();
//創建3個佈告板
CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay=new ForecastDisplay(weatherData);
//模擬氣象測量
weatherData.setMeasurements(80,65, 30.4f);
}
}
輸出結果
當前天氣狀態 :80.0度 , 65.0溼度 和 30.4帕
統計天氣顯示 :80.0度 , 65.0溼度 和 30.4帕
預測天氣顯示 :80.0度 , 65.0溼度 和 30.4帕
模式總結
觀察者模式提供了一種對象設計,讓主題和觀察者之間松耦合。讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。 但是依賴關係並未完全解除,抽象通知者依舊依賴抽象的觀察者。因爲主題在調用觀察者的update方法時把內部的成員傳給了觀察者,而且對於不同的觀察者可能需要不同的參數。Java內置了觀察者模式,有推push和拉pull兩種。後續會使用java內置的觀察者模式來實現。