观察者模式(学习笔记2020-07-17)
前言:
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。
**意图:**定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
**主要解决:**一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
**何时使用:**一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
**如何解决:**使用面向对象技术,可以将这种依赖关系弱化。
**关键代码:**在抽象类里有一个
ArrayList
存放观察者们。应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
使用场景
假设我们接到一个来自气象局的需求:气象局需要我们构建一套系统,这系统有两个公告牌,分别用于显示当前的实时天气和未来几天的天气预报。当气象局发布新的天气数据(
WeatherData
)后,两个公告牌上显示的天气数据必须实时更新。气象局同时要求我们保证程序拥有足够的可扩展性,因为后期随时可能要新增新的公告牌。这套系统中主要包括三个部分:气象站(获取天气数据的物理设备)、
WeatherData
(追踪来自气象站的数据,并更新公告牌)、公告牌(用于展示天气数据)
WeatherData
知道如何跟气象站联系,以获得天气数据。当天气数据有更新时,WeatherData
会更新两个公告牌用于展示新的天气数据。
错误示范
public class WeatherData {
//实例变量声明
...
public void measurementsChanged() {
float temperature = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
//获取预报天气
List<Float> forecastTemperatures = getForecastTemperatures();
//更新公告牌
currentConditionsDisplay.update(temperature, humidity, pressure);
forecastDisplay.update(forecastTemperatures);
}
...
}
上面典型的针对实现编程,这会导致我们以后增加或删除公告牌时必须修改程序。
使用观察者模式优化
1.0 业务概念:
这里观察者是
公告牌
, 被观察者是天气数据
观察者模式通常基于Subject和Observer接口类来设计,下面是是类图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4elJIrNE-1623604314719)(https://s1.ax1x.com/2020/07/17/UyvROS.jpg)]
结合上面的类图,我们现在将观察者模式应用到WeatherData项目中来。于是有了下面这张类图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z6A9CkLq-1623604314722)(https://s1.ax1x.com/2020/07/17/UyxVTH.jpg)]
使用上图提供的方式: https://juejin.im/post/57de12355bbb50005e648bd8#comment
1.1使用JDK
提供的类来实现的观察者模式
WeatherData
(被观察者)是如何实现的
/**
* 被观察对象(当数据发生改变时候,可以通知所有观察者)
*
* @author: ZhiHao
* @date: 2020/7/17
*/
public class WeatherData extends Observable {
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压
/**
* 数据发生修改则通知所有观察者
*
* @author: ZhiHao
* @date: 2020/7/17
*/
public void measurementsChanged() {
//调用setChagned方法,将changed域设置为true,这样才能通知到观察者们
setChanged();
//通知所有观察者
super.notifyObservers("我是通知参数");
}
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;
}
/**
* 不在通知, 从观察者名单删除
*
* @param observer
* @author: ZhiHao
* @date: 2021/5/12
*/
public void removeObserver(Observer observer) {
this.deleteObserver(observer);
}
}
显示当前天气的公告牌CurrentConditionsDisplay
(观察者角色)
/**
* 观察者(对WeatherData进行观察, WeatherData进行了修改后会通知到此观察者进行对应处理)
*
* @author: ZhiHao
* @date: 2020/7/17
*/
public class CurrentConditionsDisplay implements Observer {
/** 被观察者 */
private WeatherData weatherData;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压
public CurrentConditionsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
//通知所有观察者 (可以传递通知参数)
this.weatherData.addObserver(this);
}
public void display(Object... o) {
System.out.println("当前温度为:" + this.temperature + "℃");
System.out.println("当前湿度为:" + this.humidity);
System.out.println("当前气压为:" + this.pressure);
System.out.println("当前通知参数:" + o[0]);
}
/**
* 接收到通知后执行!
*
* @param o
* @param arg 通知时传递的参数
* @author: ZhiHao
* @date: 2020/7/17
*/
@Override
public void update(Observable o, Object arg) {
this.temperature = this.weatherData.getTemperature();
this.humidity = this.weatherData.getHumidity();
this.pressure = this.weatherData.getPressure();
display(arg);
}
}
其他省略…
1.2 进行测试
/**
* @author: ZhiHao
* @date: 2020/6/3
*/
@Test
public void test() throws Exception {
//创建被观察者天气数据, 一旦天气数据发现修改, 所有观察WeatherData的都能通知到
WeatherData weatherData = new WeatherData();
//创建观察者
CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
//发生数据修改
weatherData.setMeasurements(1,2,3);
// 移除某个观察者
weatherData.removeObserver(currentConditionsDisplay);
// 就不会在通知输出
weatherData.setMeasurements(1,2,3);
}
// -------------------consul------------------
//输出结果
//我是通知参数
//当前温度为:1.0℃
//当前湿度为:2.0
//当前气压为:3.0
1