观察者模式
定义
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
案例
天气预报项目需求,具体需求如下:
1、气象站可以将每天测量的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。需要设计开放型API,便于其他第三方也能接入气象站获取数据。
2、提供温度、气压和湿度的接口。
3、测量数据更新时,要能实时的通知给第三方。
思路分析图解(类图)
观察者模式原理
观察者模式类似订牛奶业务
- 奶站/气象局:Subject
- 用户/第三方网站:Observer
Subject:登记注册、移除和通知
- registerObserver 注册
- removeObserver 移除
- notifyObservers() 通知所有的注册用户,根据不同需求,可以是更新数据,让用户来取,也可以是实施推送,看具体需求来定。
代码实现:
//观察者接口,由观察者来实现
public interface Observer {
public void update(float temperature,float pressure,float humidity);
}
//接口,让WeatherData 来实现
public interface Subject {
public void registerObserver(Observer O);
public void removeObserver(Observer O);
public void notifyObservers();
}
/*
*类是核心
* 1.包含最新的天气情况信息
* 2.含有观察者集合,使用ArrayList管理
* 3.当数据有更新时,就主动的调用 ArrayList,通知所有的(接入方)就看到最新的信息
*/
public class WeatherData implements Subject {
private float temperature;
private float pressure;
private float humidity;
//加入新的三方
public ArrayList<Observer> observers;
public WeatherData() {
observers = new ArrayList<Observer>();
}
public float getTemperature() {
return temperature;
}
public float getPressure() {
return pressure;
}
public float getHumidity() {
return humidity;
}
public void dataChange() {
//调用接收方的update
//currentConditions.update(getTemperature(),getPressure(),getHumidity());
notifyObservers();
}
public void setData(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
}
//注册一个观察者
@Override
public void registerObserver(Observer O) {
observers.add(O);
}
//移除一个观察者
@Override
public void removeObserver(Observer O) {
if(observers.contains(O)){
observers.remove(O);
}
}
//遍历所有的观察者,并通知
@Override
public void notifyObservers() {
for(int i = 0; i<observers.size();i++){
observers.get(i).update(this.temperature,this.pressure,this.humidity);
}
}
}
public class CurrentConditions implements Observer{
//温度、气压、湿度
private float temperature;
private float pressure;
private float humidity;
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
private void display() {
System.out.println("***Today mTemperature:"+temperature+"***");
System.out.println("***Today mPressure:"+pressure+"***");
System.out.println("***Today mHumidity:"+humidity+"***");
}
}
public class BaiduSite implements Observer{
//温度、气压、湿度
private float temperature;
private float pressure;
private float humidity;
public void update(float temperature, float pressure, float humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
private void display() {
System.out.println("===========百度网站===========");
System.out.println("***百度网站 气温:"+temperature+"***");
System.out.println("***百度网站 气压:"+pressure+"***");
System.out.println("***百度网站 湿度:"+humidity+"***");
}
}
测试类
public class Client {
public static void main(String[] args) {
//创建一个WeatherData
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
BaiduSite baiduSite = new BaiduSite();
//注册到WeatherData
weatherData.registerObserver(currentConditions);
weatherData.registerObserver(baiduSite);
//测试
System.out.println("通知各个注册的观察者,看看信息");
weatherData.setData(10f,100f,30.3f);
}
}
结果:
优点
1、观察者和被观察者是抽象耦合的。
2、建立一套触发机制。
缺点
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
注意事项
1、JAVA 中已经有了对观察者模式的支持类。
2、避免循环引用。
3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。