設計模式之禪(2)-觀察者模式
文章目錄
更多關於設計模式的文章請點選:設計模式之禪(0)-目錄頁
觀察者模式是Java標準庫的開發中常用的一種設計模式,它完美的將觀察者和被觀察的物件分離開。當被觀察者(主題)改變時,所有觀察者(訂閱者)都將受到改變的情況,而無論觀察者處於什麼樣的狀態。
一、什麼是觀察者模式
觀察者模式(Observer Pattern
)完美的將觀察者和被觀察的物件分離開。舉個例子,使用者介面可以作為一個觀察者,業務資料是被觀察者,使用者介面觀察業務資料的變化,發現數據變化後,就顯示在介面上。面向物件設計的一個原則是:系統中的每個類將重點放在某一個功能上,而不是其他方面。一個物件只做一件事情,並且將他做好。觀察者模式在模組之間劃定了清晰的界限,提高了應用程式的可維護性和重用性。
觀察者設計模式定義了物件間的一種一對多的組合關係,以便一個物件的狀態發生變化時,所有依賴於它的物件都得到通知並自動重新整理。
二、通過設計氣象站模組來說明觀察者模式
2.1、觀察者模式設計的一般類圖
2.2、觀察者模式帶來的鬆耦合
當兩個物件之間鬆耦合,它們依然可以互動,但是不太清楚彼此的細節。
觀察者模式提供了一種物件設計,讓主題和觀察者之間鬆耦合。
為什麼呢?
關於觀察者的一切,主題只知道觀察者實現了某個介面(也就是Observer介面)。
主題不需要知道觀察者的具體類是誰、做了些什麼或其他任何細節。
任何時候我們都可以增加新的觀察者。因為主題唯一依賴的東西是一個實現Observer介面的物件列表,所以我們可以隨時增加觀察者。事實上,在執行時我們可以用新的觀察者取代現有的觀察者,主題不會受到任何影響。
同樣的,也可以在任何時候刪除某些觀察者。
有新型別的觀察者出現時,主題的程式碼不需要修改。假如我們有個新的具體類需要當觀察者,我們不需要為了相容新型別而修改主題的程式碼,所有要做的就是在新的類裡實現此觀察者介面,然後註冊為觀察者即可。主題不在乎別的,它只會傳送通知給所有實現了觀察者介面的物件。
我們可以獨立地複用主題或觀察者。如果我們在其他地方需要使用主題或觀察者,可以輕易地複用,因為二者並非緊耦合。
2.3、氣象站模組需求
2.4、使用觀察者模式實現氣象站模組
- Subject介面
/**
* @Description: 主題介面
* @CreateDate: Created in 2018/11/22 11:30
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface Subject {
//註冊觀察者
public void registerObserver(Observer observer);
//移除觀察者
public void removeObserver(Observer observer);
//通知所有觀察者
public void notifyObservers();
}
- Observer介面
/**
* @Description: 觀察者的介面
* @CreateDate: Created in 2018/11/22 11:32
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface Observer {
//當氣象觀測值改變時,主題會把這些狀態值當作方法的引數,傳送給觀察者。
public void update(float temp,float humidity,float pressure);
}
- DisplayElement類
/**
* @Description: DisplayElement介面只包含了一個方法,也就是display()。當佈告板需要顯示時,呼叫此方法。
* @CreateDate: Created in 2018/11/22 11:36
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public interface DisplayElement {
public void display();
}
- WeatherData類
/**
* @Description:
* @CreateDate: Created in 2018/11/22 11:38
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temp;
private float hudimity;
private float pressure;
public WeatherData(){
observers = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int i = observers.indexOf(observer);
if(i >= 0){
observers.remove(observer);
}else{
System.out.println("can not delete--該主題沒有被該觀察者訂閱!");
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temp,hudimity,pressure);
}
}
public void changed(){
notifyObservers();
}
public void setMeasurements(float t,float h,float p){
this.temp = t;
this.hudimity = h;
this.pressure = p;
changed();
}
}
- CurrentConditionDisplay類
/**
* @Description: 當前天氣狀況公告板
* @CreateDate: Created in 2018/11/22 11:48
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class CurrentConditionDisplay implements Observer,DisplayElement{
private float temp;
private float humidity;
private float pressure;
private Subject subject;
public CurrentConditionDisplay(Subject s){
this.subject = s;
//在該主題中註冊該觀察者,接受主題釋出的變化
subject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
//更新完畢,輸出更新後的狀態
display();
}
@Override
public void display() {
System.out.println("當前天氣狀況:");
System.out.println("temp :"+temp+"\t humidity :"+humidity+"\t pressure :"+pressure);
}
}
- 測試氣象站模組
/**
* @Description: 測試觀察者模式構建的氣象站能否使用
* @CreateDate: Created in 2018/11/22 11:55
* @Author: <a href="https://blog.csdn.net/pbrlovejava">arong</a>
*/
public class TestWeatherData {
@Test
public void TestObserverPattern(){
WeatherData weatherData = new WeatherData();
//三個觀察者去訂閱weatherData主題,當主題有所改變,觀察者會接受到改變
CurrentConditionDisplay conditionDisplay1 = new CurrentConditionDisplay(weatherData);
CurrentConditionDisplay conditionDisplay2 = new CurrentConditionDisplay(weatherData);
CurrentConditionDisplay conditionDisplay3 = new CurrentConditionDisplay(weatherData);
//主題移除掉一個觀察者
weatherData.removeObserver(conditionDisplay1);
//更新資料
weatherData.setMeasurements(1.0f,2.0f,3.0f);
weatherData.setMeasurements(3.0f,2.0f,1.0f);
}
}