观察者模式
认识观察者模式:
早些年信息还不是很发达的时候,人们获取信息的途径主要是通过看报纸的形式。今天介绍的观察者模式用报社来作比喻最合适不过了。报社的业务就是出版报纸,顾客向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订阅用户,你就会一直收到新报纸。当你不想看报纸的时候,取消订阅,他们就不会再送新报纸来。只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。
出版者+订阅者 = 观察者模式
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式通常由四个部分组成:主题接口(对象使用主题接口注册成为观察者,或者把自己从观察者中删除)、具体的主题 对象(一个具体的主题总是实现主题接口,除了注册和撤销方法之外,具体主题还实现了notifyObserver()方法,此方法用于在状态改变时更新所有当前观察者。)、观察者接口(所有潜在的观察者必须实现观察者接口,这个接口只有update()一个方法,当主题状态改变时它被调用。)、具体的观察者对象 (具体的观察者可以实现此接口的任意类。观察者必须注册具体主题,以便接收更新。)
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
上代码:
主题接口
package com.my.observer;
public interface Subject {
//这两个方法都需要一个观察者作为变量,该观察者是用来注册或被删除的。
public void registerObserver(Observer o);
public void removeObserver(Observer o);
//当主题状态改变时,这个方法会被调用,以通知所有的观察者。
public void notifyObserver();
}
观察者接口
package com.my.observer;
public interface Observer {
//当气象观测值改变时,主题会把这些状态值当作方法的参数,传递给观察者。
public void update(float temp,float humidity, float pressure);
}
显示接口
package com.my.observer;
public interface DisplayElement {
//当布告板需要显示时,调用此方法。
public void display();
}
具体的主题对象
package com.my.observer;
import java.util.ArrayList;
public class WeatherData implements Subject {
//增加ArrayList来记录观察者,此ArrayList是在构造器中建立的。
private ArrayList observers;
private float temperature;
private float hmidity;
private float pressure;
public WeatherData(){
observers = new ArrayList();
}
public void registerObserver(Observer o){
//当注册观察者时,我们只要把它加到ArrayList后面即可。
observers.add(o);
}
public void removeObserver(Observer o){
//当观察者想取消注册,我们把它从ArrayList中删除即可。
int i = observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
public void notifyObserver(){
//把状态告诉每一个观察者,因为观察者已经实现了update方法,所以我们知道如何通知他们。
for(int i=0;i<observers.size();i++){
Observer observer = (Observer) observers.get(i);
observer.update(temperature,hmidity,pressure);
}
}
public void measurementsChanged(){
//当从气象站获取更新的数据时通知观察者
notifyObserver();
}
public void setMeasurements(float temperature,float hmidity, float pressure){
//模拟从装置中读取气象站数据
this.temperature = temperature;
this.hmidity = hmidity;
this.pressure = pressure;
measurementsChanged();
}
}
具体的观察者对象
package com.my.observer;
public class CurrentConditionsDisplay implements Observer,DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
//构造器需要weatherData对象作为注册用。
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature,float humidity,float pressure){
//当update()被调用时,我们把温度和湿度保存起来,然后调用display()。
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display(){
//打印温度和湿度值。
System.out.println("Current conditions:" + temperature +
"F degrees and"+ humidity + "%humidity");
}
}
测试程序
package com.my.observer;
public class WeatherStation {
public static void main(String[] args){
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay =
new CurrentConditionsDisplay(weatherData);
//模拟获取气象数据
weatherData.setMeasurements(80,65,30.4f);
weatherData.setMeasurements(82,70,29.3f);
}
}
run:
使用JDK中的支持进行改造:
改造WeatherData使用java.util.Observable
package com.my.observer_inside;
import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float hmidity;
private float pressure;
public WeatherData(){
//构造器不再需要为了记住观察者们而建立数据结构了。
}
public void measurementsChanged(){
setChanged();
notifyObservers();
}
public void setMeasurements(float temperature,float hmidity, float pressure){
//模拟从装置中读取气象站数据
this.temperature = temperature;
this.hmidity = hmidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature(){
return temperature;
}
public float getHmidity(){
return hmidity;
}
public float getPressure(){
return pressure;
}
}
重做CurrentConditionsDisplay
package com.my.observer_inside;
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer,DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs,Object arg){
if(obs instanceof WeatherData){
WeatherData weatherData = (WeatherData) obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHmidity();
display();
}
}
public void display(){
//打印温度和湿度值。
System.out.println("Current conditions:" + temperature +
"F degrees and"+ humidity + "%humidity");
}
}
run: