先来认识一下观察者模式
观察者模式(Observer),又叫发布-订阅模式(Publish/Subscribe),定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
一、观察者模式项目需求
天气预报项目需求,具体要求如下:
- 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)
- 需要设计开放型 API,便于其他第三方也能接入气象站获取数据
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
1.1 天气预报设计方案 1-普通方案
- WeatherData 类
- 传统的设计方案
普通方案代码示例:
- /**
- 显示当前天气情况(可以理解成是气象站自己的网站)
- */
- public class CurrentConditions {
- // 温度,气压,湿度
- private float temperature;
- private float pressure;
- private float humidity;
-
- // 更新天气情况,是由WeatherData 来调用,使用推送模式
- public void update(float temperature, float pressure, float humidity) {
- this.temperature = temperature;
- this.pressure = pressure;
- this.humidity = humidity;
- display();
- }
-
- // 显示
- public void display() {
- System.out.println("***Today mTemperature: " + temperature + "****");
- System.out.println("***Today mPressure: " + pressure + "****");
- System.out.println("***Today mHumidity: " + humidity + "****");
- }
- }
- /**
- * 类是核心
- * 1、包含最新的天气情况信息
- * 2、含有 CurrentConditions 对象
- * 3、当数据有更新时,就主动的调用 CurrentConditions 对象update方法(含display),这样他们(接入方)就看到最新的信息
- */
- public class WeatherData {
- // 温度,气压,湿度
- private float temperature;
- private float pressure;
- private float humidity;
- private CurrentConditions currentConditions;
-
- public WeatherData(CurrentConditions currentConditions) {
- this.currentConditions = currentConditions;
- }
-
- public float getTemperature() {
- return temperature;
- }
-
- public float getPressure() {
- return pressure;
- }
-
- public float getHumidity() {
- return humidity;
- }
-
- public void dataChange() {
- // 调用 接入方的 update
- currentConditions.update(getTemperature(), getPressure(), getHumidity());
- }
-
- // 当数据有更新时,就调用setData
- public void setData(float temperature, float pressure, float humidity) {
- this.temperature = temperature;
- this.pressure = pressure;
- this.humidity = humidity;
- // 调用dataChange,将最新的信息,推送给接入方 currentConditions
- dataChange();
- }
- }
- public class Client {
- public static void main(String[] args) {
- // 创建接入方 currentConditions
- CurrentConditions currentConditions = new CurrentConditions();
- // 创建 WeatherData 并将接入方 currentConditions 传递到WeatherData中
- WeatherData weatherData = new WeatherData(currentConditions);
-
- // 更新天气情况
- weatherData.setData(30, 150, 40);
-
- // 天气情况变化
- System.out.println("========== 天气情况变化 ==========");
- weatherData.setData(40, 160, 20);
- }
- }
在 WeatherData 中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到dataChange,不利于维护,也不是动态加入
- public void dataChange() {
- currentConditions.update(getTemperature(), getPressure(), getHumidity());
- }
二、观察者模式原理
- 观察者模式类似订牛奶业务
- 奶站 / 气象局:Subject
- 客户 / 第三方网站:Observer
Subject:登记注册、移除和通知
- registerObserver 注 册
- removeObserver 移 除
- notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送
Observer:接收输入
观察者模式:对象之间多对一依赖的一种设计方案,被依赖的对象为 Subject,依赖的对象为Observer,Subject
通知 Observer 变化,比如这里的奶站是 Subject,是 1 的一方。用户时 Observer,是多的一方
2.1 观察者模式解决天气预报需求
类图说明
观察者模式方案:
- // 观察者接口,由观察者来实现
- 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();
- }
- /**
- 显示当前天气情况(可以理解成是气象站自己的网站)
- */
- public class CurrentConditions implements Observer {
- // 温度,气压,湿度
- private float temperature;
- private float pressure;
- private float humidity;
-
- // 更新天气情况,是由WeatherData 来调用,使用推送模式
- public void update(float temperature, float pressure, float humidity) {
- this.temperature = temperature;
- this.pressure = pressure;
- this.humidity = humidity;
- display();
- }
-
- // 显示
- public 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;
-
- // 更新天气情况,是由WeatherData 来调用,使用推送模式
- public void update(float temperature, float pressure, float humidity) {
- this.temperature = temperature;
- this.pressure = pressure;
- this.humidity = humidity;
- display();
- }
-
- // 显示
- public void display() {
- System.out.println("========百度网站============");
- System.out.println("*** 百度网站 mTemperature: " + temperature + "****");
- System.out.println("*** 百度网站 mPressure: " + pressure + "****");
- System.out.println("*** 百度网站 mHumidity: " + humidity + "****");
- }
- }
- /**
- * 类是核心
- * 1、包含最新的天气情况信息
- * 2、含有 观察者集扣,使用ArrayList管理
- * 3、当数据有更新时,就主动的调用 ArrayList,通知所有的(接入方)就看到最新的信息
- */
- public class WeatherData implements Subject {
- // 温度,气压,湿度
- private float temperature;
- private float pressure;
- private float humidity;
-
- // 观察者集合
- private ArrayList
observers; -
- public WeatherData() {
- observers = new ArrayList<>();
- }
-
- 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();
- }
-
- // 当数据有更新时,就调用setData
- public void setData(float temperature, float pressure, float humidity) {
- this.temperature = temperature;
- this.pressure = pressure;
- this.humidity = humidity;
- // 调用dataChange,将最新的信息,推送给接入方 currentConditions
- dataChange();
- }
-
- // 注册一个观察者
- @Override
- public void registerObserver(Observer o) {
- observers.add(o);
- }
-
- // 移除一个观察者
- @Override
- public void removeObserver(Observer 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 Client {
- public static void main(String[] args) {
- // 创建一个WeatherData
- WeatherData weatherData = new WeatherData();
-
- // 创建观察者
- CurrentConditions currentConditions = new CurrentConditions();
-
- // 注册到weatherData
- weatherData.registerObserver(currentConditions);
- weatherData.registerObserver(new BaiduSite());
-
- weatherData.removeObserver(currentConditions);
-
- // 测试
- System.out.println("通知各个注册的观察者,看看信息");
- weatherData.setData(10, 100, 30.3f);
- }
- }
2.2 观察者模式的好处
- 观察者模式设计后,会以集合的方式来管理观察乾 (Observer),包括注册,移除和通知
- 这样,我们增加观察者 (这里可以理解成一个新的客户接入进来),就不需要去修改核心业务类 WeatherData 不会修改代码, 遵守了 ocp 原则
三、观察者模式在 Jdk 应用的源码分析
- Jdk 的 Observable 类就使用了观察者模式
- 代码分析+模式角色分析
模式角色分析
- Observable 的作用和地位等价于 我们前面讲过 Subject
- Observable 是类,不是接口,类中已经实现了核心的方法,即管理 Observer 的方法 add.. delete .. notify...
- Observer 的作用和地位等价于我们前面讲过的 Observer,有 update
- Observable 和 Observer 的使用方法和前面讲过的一样,只是 Observable 是类,通过继承来实现观察者模式
四、分析观察者模式
4.1 观察者模式的主要优点
- 观察者和被观察者是抽象耦合的
- 建立了一套触发机制
4.2 观察者模式的缺点
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
- 如果观察者和观察目标间有循环依赖,可能导致系统崩溃
- 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的
4.3 观察者模式的使用场景
- 关联行为场景
- 事件多级触发场景
- 跨系统的消息变换场景,如消息队列的处理机制
4.4 观察者模式应用实例
- 手机丢了,委托别人给其他人发消息通知
- 通知老师/老板来了
- 拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
- 在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者
- 猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者
4.5 观察者模式使用注意事项
- 避免循环引用
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式
当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象有待改变的时候,应该考虑使用观察者模式。
而使用观察者模式的动机在于:将一个系统分割成一系列相互协作的类有一个很不好的副作用,就是需要维护相关对象间的一致性,我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便,而观察者模式所做的工作就是在解除耦合。
文章最后,给大家推荐一些受欢迎的技术博客链接:
- JAVA相关的深度技术博客链接
- Flink 相关技术博客链接
- Spark 核心技术链接
- 设计模式 —— 深度技术博客链接
- 机器学习 —— 深度技术博客链接
- Hadoop相关技术博客链接
- 超全干货--Flink思维导图,花了3周左右编写、校对
- 深入JAVA 的JVM核心原理解决线上各种故障【附案例】
- 请谈谈你对volatile的理解?--最近小李子与面试官的一场“硬核较量”
- 聊聊RPC通信,经常被问到的一道面试题。源码+笔记,包懂
- 深入聊聊Java 垃圾回收机制【附原理图及调优方法】
欢迎扫描下方的二维码或 搜索 公众号“大数据高级架构师”,我们会有更多、且及时的资料推送给您,欢迎多多交流!
评论记录:
回复评论: