概述
觀察者模式是一種很常用的設計模式,Android中的廣播(Broadcast)就是用觀察者模式設計的,再往大一步,諸如微博這種社交平臺也是用的觀察者模式,觀察者模式亦被稱作發佈-訂閱模式。觀察者模式包含兩個要素:目標對象、觀察者對象。其中,當目標對象的狀態發生改變時,它所依賴的觀察者將立即得到通知,通知攜帶的數據將在消息中心得到處理。
內容
1、使用觀察者模式的場景
當一個對象的狀態需要被多個對象瞭解,以保證高度協作。
2、觀察者模式的結構
3、觀察者模式的實現
假設有一個氣象監測模型,那麼Observer是需要獲取氣象通知的觀察者,Subject則是氣象主題,作爲被觀察者。
3.1、主題接口
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
主題的三個核心功能即是:註冊觀察者、刪除觀察者、通知觀察者。所以主題接口務必抽象這三個方法。
3.2、觀察者接口
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
當氣象狀態變化時,主題會把相關狀態值作爲方法參數傳送給觀察者。
3.3、顯示接口
這個和觀察者模式無關。
public interface DisplayElement {
public void display();
}
3.4、實現主題接口
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
this.observers = new ArrayList();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
public void notifyObservers() {
for (Observer o : observers) {
o.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();
}
}
除了實現Subject接口的三方法外,WeatherData中可以有自己特定的方法,用以實現具體的功能,如setMeasurements(),用以改變觀察值。
在實際運用中,當氣象狀態信息真實變化,或狀態需被按時發佈時,這個方法將被調用。
3.5、實現觀察者接口
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
public CurrentConditionsDisplay() {
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
Log.d("display", "Current conditions:" + temperature + "F degrees and" + humidity + "% humidity");
}
}
這裏的觀察者類型是CurrentConditionsDisplay,實現了Observer接口的update()方法,和DisplayElement的display()方法。
從WeatherData類中可以看到,update()方法在WeatherData類型的對象的數據改變時被調用,作爲觀察者的CurrentConditionsDisplay類型對象將獲得最新的數據,接着調用display()展示數據。但觀察者能獲取主題所發佈的新數據的前提是,觀察者綁定了主題,且未被主題刪除。
public class Test {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay display = new CurrentConditionsDisplay();
weatherData.registerObserver(display);
weatherData.setMeasurements(80, 65, 30.4f);
//weatherData.removeObserver(display);
}
}
主通過registerObserver()方法,主題註冊了觀察者,這一步即是訂閱。
當主題調用setMeasurements()方法,就會通知觀察者最新消息,這一步即是發佈。
4、java 內置的觀察者接口
實現java.util.Observable接口,然後調用兩個方法:
先調用setChanged(),標記狀態已經改變;
然後調用notifyObservers()方法或notifyObservers(Object arg)方法,通知觀察者,如果調用notifyObservers(Object arg)方法,其參數會被傳送給觀察者。
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() {
}
public void measurementsChanged() {
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = pressure;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
觀察者實現:
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
public CurrentConditionsDisplay() {
}
@Override
public void display() {
Log.d("display", "temperature:" + temperature + ",humidity:" + humidity);
}
@Override
public void update(Observable o, Object arg) {
if (o instanceof WeatherData) {
WeatherData weatherData = (WeatherData) o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
}
使用範例:
public class Test {
public static void main(String[] args){
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay display = new CurrentConditionsDisplay();
weatherData.addObserver(display);
weatherData.setMeasurements(80, 65, 30.4f);
//weatherData.deleteObserver(display); //刪除觀察者
}
}
不過是方法名變了。
結語
如果這篇博客幫助到您,您可以完成以下操作:
在網易雲搜索“星河河”->歌手->點擊進入(您將進入我的網易雲音樂人賬號星河河)->關注我->多聽聽我的歌。