定义
观察者模式又称为发布-订阅模式。定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。
在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。
角色
-
Subject:目标。他把所有对观察者对戏的引用保存在一个聚集里,每一个主题都可以有多个观察者。
-
Observer:观察者。为所有的具体观察者定义一个接口,在得到主题的通知时能够及时的更新自己。
-
ConcreteSubject:具体主题。将有关状态存入具体观察者对象。在具体主题发生改变时,给所有的观察者发出通知。
-
ConcreteObserver:具体观察者。实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态相协调。
优缺点
优点:
-
对象之间松耦合
-
观察者模式支持“广播通信”。主题会向所有的观察者发出通知。
-
观察者模式符合“开闭原则”的要求。
缺点:
-
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
-
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进 行循环调用,可能导致系统崩溃。
-
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
实例
需求:
在气象观测站中,它能够追踪目前的天气状况,包括温度、适度、气压。需要实现一个布告板,能够分别显示目前的状态,气象统计和简单的预报。当气象站中获取最新的测量数据时,三种布告板必须实时更新。
观察者接口:
/**
* 观察者接口
*/
public interface Observer {
/**
* 更新操作
* @param temp
* @param humidity
* @param pressure
*/
void update(float temp,float humidity,float pressure);
}
主体接口:
/**
* 主体接口
*/
public interface Subject {
/**
* 注册观察者
* @param observer
*/
void registerObserver(Observer observer);
/**
* 删除观察者
* @param observer
*/
void removeOberver(Observer observer);
/**
* 当主题状态发生改变时,这个方法需要被调用,以通知所有观察者
*/
void notifyObserver();
}
布告板显示接口:
/**
* 布告板显示接口
*/
public interface DisplayElement {
/**
* 显示
*/
void display();
}
天气:
/**
* 天气
*/
public class WeatherData implements Subject {
private List<Observer> observers;
private float tempterature;
private float pressure;
private float humidity;
public WeatherData(){
observers = new ArrayList<Observer>();
}
@Override
public void notifyObserver() {
for(int i = 0; i < observers.size();i++){
Observer observer = observers.get(i);
observer.update(tempterature, humidity, pressure);
}
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeOberver(Observer observer) {
int i = observers.indexOf(observer);
if(i >= 0){
observers.remove(i);
}
}
/**
* 气象站得到更新的观测数据时,通知观察者
*/
public void measurementChanged(){
notifyObserver();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.tempterature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementChanged();
}
}
布告板
/**
* 布告板
*/
public class CurrentConditionsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
// 注册观察者
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
this.temperature = temp;
this.humidity = humidity;
display();
}
@Override
public void display() {
System.out.println("温度:"+temperature+"F 湿度:"+humidity+"%");
}
}
测试:
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 78, 40.4f);
}
控制台输出:
温度:80.0F 湿度:65.0%
温度:82.0F 湿度:70.0%
温度:78.0F 湿度:78.0%