17.java设计模式之观察者模式
基本需求:
- 气象站可以将每天测量到的温度,湿度,气压等等,以公告的形式发布出去(比如发布到自己的网站或第三方)
- 需要设计开放型API,便于其他第三方也能接入气象站获取数据
- 提供温度、气压和湿度的接口
- 测量数据更新时,要能实时的通知给第三方
传统方案:
通过对需求的分析,我们可以设计一个WeatherData类,其中包含getXxx()方法,可以让第三方接入,并得到相关信息
当有数据更新时,WeatherData调用dataChange()方法去更新数据,当第三方获取时,就能获取到最新的数据,当然也可以推送
WeatherData内部还需维护一个CurrentCondition对象,便于完成推送或通知
UML类图
代码实现
public class CurrentCondition { // 被通知的对象类 // 温度
private double temperature;
// 压力
private double pressure;
// 湿度
private double humidity; public void update(double temperature, double pressure, double humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
} private void display() {
System.out.println("======CurrentCondition======");
System.out.println("温度:" + this.temperature);
System.out.println("压力:" + this.pressure);
System.out.println("湿度:" + this.humidity);
} }
public class WeatherData { // 天气数据类 // 温度
private double temperature;
// 压力
private double pressure;
// 湿度
private double humidity; // 聚合天气数据发生改变需要通知或者被推送的类
private CurrentCondition currentCondition; public WeatherData(CurrentCondition currentCondition) {
this.currentCondition = currentCondition;
} // 将天气数据的改变通知给需要通知或者被推送的类 被通知的类也可以通过getXxx()方法自己获取数据
private void dataChange() {
currentCondition.update(this.temperature, this.pressure, this.humidity);
} // 更改天气数据
public void setData(double temperature, double pressure, double humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
dataChange();
} }
public class Client {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData(new CurrentCondition());
// 更新天气数据 就会通知应用或者第三方
weatherData.setData(10d, 20d, 30d);
System.out.println("======天气情况发生变换======");
weatherData.setData(20d, 30d, 40d);
}
}
问题分析
- 其他第三方接入气象站获取数据问题
- 无法在运行时动态的添加第三方
- 违反了ocp原则,在WeatherData中,如果需要增加新的第三方应用,都需要创建一个对应的第三方的公告板对象,并加入到dataChange()方法中,不利于维护,也不是动态加入
基本介绍:
当对象间存在一对多关系时,则使用观察者模式(Observer),比如,当一个对象被修改时,则会自动通知依赖它的对象,观察者模式属于行为型模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知
基本原理
- 观察者模式类似订牛奶业务 例如:奶站/气象局:Subject 和 用户/第三方网站:Observer
- Subject:登记注册、移除和通知 一的一方(被观察者)
- registerObserver 注册
- removeObserver 移除
- notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送,看具体需求定
- Observer:接收输入 也就是第三方,可以有多个实现 多个一方(观察者)
- 对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化,比如这里的奶站是Subject,是1的一方。用户时Observer,是多的一方
UML类图(案例)
代码实现
public interface Subject { // 被观察者接口 作为一的一方 ,需要聚合多个观察者 可以使用List进行管理 // 注册观察者
void register(Observer observer); // 移除观察者
void remove(Observer observer); // 被观察者状态发生改变,进行通知所有的观察者
void notifyObservers(); } // 实现类
class WeatherData implements Subject { // 天气数据类 作为被观察者 一的一方 // 温度
private double temperature;
// 压力
private double pressure;
// 湿度
private double humidity; // 聚合所有的观察者进行管理 动态进行修改 使用List集合管理观察者们
private List<Observer> observers; public WeatherData() {
this.observers = new ArrayList<Observer>();
} // 更改天气数据
public void setData(double temperature, double pressure, double humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
// 被观察者状态发生改变,通知注册的所有观察者
notifyObservers();
} @Override
public void register(Observer observer) {
if (!this.observers.contains(observer)) {
observers.add(observer);
}
} @Override
public void remove(Observer observer) {
this.observers.remove(observer);
} @Override
public void notifyObservers() {
// 遍历观察者的集合 对所有的观察者进行通知
observers.forEach(observer -> observer.update(this.temperature, this.pressure, this.humidity));
} }public interface Observer { // 观察者接口 作为多的一方 // 被观察者改变状态时,通知观察者所调用的方法
void update(double temperature, double pressure, double humidity); } // 子类一
class CurrentCondition implements Observer{ // 被通知的对象类 // 温度
private double temperature;
// 压力
private double pressure;
// 湿度
private double humidity; public void update(double temperature, double pressure, double humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
} private void display() {
System.out.println("======CurrentCondition======");
System.out.println("温度:" + this.temperature);
System.out.println("压力:" + this.pressure);
System.out.println("湿度:" + this.humidity);
} } // 子类二
class BaiDu implements Observer{ // 被通知的对象类 // 温度
private double temperature;
// 压力
private double pressure;
// 湿度
private double humidity; public void update(double temperature, double pressure, double humidity) {
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
} private void display() {
System.out.println("======BaiDu======");
System.out.println("温度:" + this.temperature);
System.out.println("压力:" + this.pressure);
System.out.println("湿度:" + this.humidity);
} }
public class Client {
public static void main(String[] args) {
// 创建观察者
CurrentCondition currentCondition = new CurrentCondition();
// 创建被观察者
WeatherData weatherData = new WeatherData();
// 向被观察者中注册观察者
weatherData.register(currentCondition);
// 改变被观察者的状态 观察者就会收到通知
weatherData.setData(10d, 20d, 30d); // 后续业务扩展 加入新的第三方
BaiDu baiDu = new BaiDu();
weatherData.register(baiDu);
System.out.println("======加入了新的第三方======");
weatherData.setData(20d, 30d, 40d);
}
}
jdk源码:
在jdk的Observer类和Observable类就使用到了观察者模式
Observer作为了观察者接口,提供了update()方法
Observable相当于Subject,作为被观察者,只不过在jdk源码中,Observable是一个类,而不是接口,提供了注册观察者,删除观察者,通知观察者的一系列管理观察者的方法
// 观察者接口
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
} // 被观察者 相当于Subject
public class Observable {
private boolean changed = false;
private Vector<Observer> obs; /** Construct an Observable with zero Observers. */ public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
} /**
* Deletes an observer from the set of observers of this object.
* Passing <CODE>null</CODE> to this method will have no effect.
* @param o the observer to be deleted.
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
} /**
* If this object has changed, as indicated by the
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to
* indicate that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and <code>null</code>. In other
* words, this method is equivalent to:
* <blockquote><tt>
* notifyObservers(null)</tt></blockquote>
*
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers() {
notifyObservers(null);
} /**
* If this object has changed, as indicated by the
* <code>hasChanged</code> method, then notify all of its observers
* and then call the <code>clearChanged</code> method to indicate
* that this object has no longer changed.
* <p>
* Each observer has its <code>update</code> method called with two
* arguments: this observable object and the <code>arg</code> argument.
*
* @param arg any object.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#hasChanged()
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal; synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
} for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
}
注意事项:
- 观察者和被观察者是抽象耦合的,建立一套触发机制
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
- JAVA中已经有了对观察者模式的支持类
- 避免循环引用
- 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式
17.java设计模式之观察者模式的更多相关文章
- 理解java设计模式之观察者模式
在生活实际中,我们经常会遇到关注一个事物数据变化的情况,例如生活中的温度记录仪,当温度变化时,我们观察它温度变化的曲线,温度记录日志等.对于这一类问题,很接近java设计模式里面的“观察者模式”,它适 ...
- java设计模式之观察者模式以及在java中作用
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...
- java设计模式02观察者模式
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 这里主要讲一下学习内置观察者的记录,在JA ...
- java设计模式之观察者模式
观察者模式 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Listener)模式或从属者模式)是软件设计模式的一种.在此种模 ...
- JAVA设计模式 之 观察者模式
简介: 观察者模式是JDK中最多的设计模式之一,非常有用,观察者模式介绍了一对多的依赖关系及松耦合,有了观察者,你将会消息灵通. 认识观察者模式,看一个报纸.杂志订阅是怎么回事: (1). 报社的业务 ...
- 折腾Java设计模式之观察者模式
观察者模式 Define a one-to-many dependency between objects where a state change in one object results in ...
- java设计模式之-观察者模式(发布-订阅模式)
1.观察者模式定义 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 2.观察者模式结构 ...
- JAVA设计模式之观察者模式 - Observer
有趣的事情发生时,可千万别错过了!有一个模式可以帮你的对象知悉现况,不会错过该对象感兴趣的事.对象甚至在运行时可决定是否要继续被通知.有了观察者,你将会消息灵通. 介绍 观察者模式的定义: 在对象之间 ...
- JAVA设计模式 之 观察者模式(JDK内置实现)
简介:使用JAVA内置的帮你搞定观察者模式. 1. 先把类图放在这里: (1). Observable类追踪所有的观察者,并通知他们. (2). Observer这个接口看起来很熟悉,它和我们之前写的 ...
随机推荐
- 【面经】面试官:如何以最高的效率从MySQL中随机查询一条记录?
写在前面 MySQL数据库在互联网行业使用的比较多,有些小伙伴可能会认为MySQL数据库比较小,存储不了很多的数据.其实,这些小伙伴是真的不了解MySQL.MySQL的小不是说使用MySQL存储的数据 ...
- hashmap源码面试分析
HashMap源码问题解析 问: 说一说对hash的理解 答: hash是对任意长度的输入输出为相同长度的输出 问: hash算法的问题 答: hash冲突问题 问: hash冲突是否可以避免 答: ...
- (1)ASP.NET Core3.1 Ocelot介绍
1.简介 Ocelot原本设计仅为与.NET Core一起使用的,它是一个.NET API网关,作为面向使用.NET运行微型服务/面向服务的体系结构需要统一的系统入口点,即当客户端(Web站点,手机A ...
- JVM 整体回顾(一)
JAVA虚拟机整体的回顾,为提纲类型 JAVA虚拟机是装在操作系统之上的一个应用软件[平台性质],作用是:将class格式的字节码编译成可执行的机器码.从而,class格式和物理机无关.也就是所谓的j ...
- 一个名为不安全的类Unsafe
最近为了更加深入了解NIO的实现原理,学习NIO的源码时,遇到了一个问题.即在WindowsSelectorImpl中的 pollWrapper属性,当我点进去查看它的PollArrayWrapper ...
- leetcode131:letter-combinations-of-a-phone-number
题目描述 给出一个仅包含数字的字符串,给出所有可能的字母组合. 数字到字母的映射方式如下:(就像电话上数字和字母的映射一样) Input:Digit string "23"Outp ...
- Boost.Accumulators累加器的简单使用
Boost.Accumulators是一个累加器,实现的功能很简单,就是对一组数据进行操作,然后可以得到一些特征数据. 由于累加器默认不对数据进行储存操作,所以不能把它作为一个简单的容器使用. 简单使 ...
- RPA小结
1--怎么理解RPA? 1)RPA就是机器人流程自动化,根据业务的固定规则,自动完成一些任务(如数据抓取,信息录入,数据处理,自动化运维等),替代人类的重复劳动, 但RPA的发展已经不仅仅满足于此,正 ...
- 企业网络拓扑MSTP功能实例(二)
组网图形 MSTP简介 以太网交换网络中为了进行链路备份,提高网络可靠性,通常会使用冗余链路.但是使用冗余链路会在交换网络上产生环路,引发广播风暴以及MAC地址表不稳定等故障现象,从而导致用户通信质量 ...
- nginx&http 第二章 ngx 事件event配置等初始化
event事件模块,配置分为两层:ngx_events_module 事件模块 和 ngx_event_core_module 事件核心模块.ngx_events_module:模块类型NGX_COR ...