定義
定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新。
設計原則
為了交互對象之間的鬆耦合設計而努力(鬆耦合的設計之所以能讓我們建立有彈性的OO系統,能夠應對變化,是因為對象之間的互相依賴降到了最低)。
實例
氣象監測應用,氣象站數據更新後更新佈告板的數據(溫度、溼度、氣壓),佈告板根據自己的需求顯示,佈告板的數量和種類可動態增加。
實現
有兩種實現方式,一種是氣象站(主題)主動推送的方式,另一種是佈告板(觀察者)主動獲取的方式。主動推送的方式只需要觀察者實現指定接口,並且註冊到主題,觀察者就能收到推送內容,這樣能隨意的增加觀察者,但難免觀察者會收到一些垃圾數據。觀察者主動獲取的方式雖然能按需獲取數據,但主題的數據暴露出來會不太安全。兩種方式各有優缺點,我們按實際情況選擇,下面實現的是氣象站主動推送的方式:
1. 新建被觀察者(主題)接口
public interface Subject {
public void registerObserver(Observer o); //註冊觀察者
public void removeObserver(Observer o); //移除觀察者
public void notifyObservers(); //通知觀察者
}
2. 氣象站實現類
public class WeatherData implements Subject {
private ArrayList<Observer> observerlist;//觀察者列表
private float temperature;//溫度
private float humidity;//溼度
private float pressure;//qiya
public WeatherData(){
observerlist = new ArrayList<>();
}
public void registerObserver(Observer o) {//註冊觀察者
observerlist.add(o);
}
public void removeObserver(Observer o) {//移除觀察者
int i = observerlist.indexOf(o);
if(i>0){
observerlist.remove(o);
}
}
public void notifyObservers() {//通知觀察者更新數據
observerlist.stream().forEach(v->v.update(temperature, humidity, pressure));
}
public void setMeasurements(float temperature, float humidity, float pressure){//數據更新
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {//獲取溫度
return temperature;
}
public float getHumidity() {//獲取溼度
return humidity;
}
public float getPressure() {//獲取氣壓
return pressure;
}
public void measurementsChanged(){
notifyObservers();
}
}
3. 新建觀察者接口
public interface Observer {//觀察者數據更新,供被觀察者調用
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {//觀察者diy顯示
public void display();
}
4. 創建佈告板
public class CurrentConditionsDisplay implements DisplayElement,Observer {
private float temperature;
private float humidity;
private float pressure;
private WeatherData weatherData;
public CurrentConditionsDisplay(WeatherData 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;
display();
}
@Override
public void display() {//diy顯示
System.out.println("current conditions: " +temperature+"F degrees and "+ humidity+"% humidity and "+pressure+ "bps");
}
}
5.測試
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();//註冊主題
//新建觀察者
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(1f,2f,3f);
}
}
輸出:
current conditions: 1.0F degrees and 2.0% humidity and 3.0bps
總結
從實例看出,氣象站是被觀察者,佈告板是觀察者,我們找出需求中變化的部分(佈告板),然後將其和固定不變的的方面(氣象站)相分離,這樣很好地實現了鬆耦合,可以動態增加布告板。以上實例自主實現觀察者模式,其實jdk中內置了該模式,我們也可以直接使用,這裡就不一一實現,但原理都是一樣的。