概述
目录
一、天气预报项目需求分析
二、方案实现
1.普通方案
2.改进方案---观察者模式
三、观测者模式在JDK源码中的应用
四、观察者模式在Spring 中的应用
基于Spring 的event实现事件发布与监听
定义事件类
定义事件监听者
定义事件发布者
初始化Spring 容器
基于Guava API轻松实现观察者模式
一、天气预报项目需求分析
天气预报的需求,具体要求如下:
1. 气象站可以将每天测到的温度、湿度、气压、PM2.5等以公告的形式发布到自己的网站或者第三方。
2. 有对外的接口可以被其他系统所调用,比如新浪等能够接入该接口来获取数据。
3. 提供关键数据的接口,温度、湿度、第三方等信息。
4.测量数据更新时,要能实时地通知给第三方。
二、方案实现
1.普通方案
currentConditions类主要负责显示当前的天气信息,接收气象站(weatherData)发过来新的温度、湿度等信息并更新当前信息。
package com.exam.observer;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午6:50:20
*当前情况类
*/
public class CurrentConditions {
private String temperature;
private String humidity;
private String pressure;
/**
*
* 显示天气信息
*/
private void display() {
System.out.println("当前温度为:"+this.temperature);
System.out.println("当前湿度为:"+this.humidity);
System.out.println("当前大气压为:"+this.pressure);
}
/**
*更新天气信息,更新完后将天气信息显示出来
*/
public void update(String temperature,String humidity,String pressure) {
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
}
WeatherData类(气象站),主要提供获取信息的接口,如果气象站的信息发生变化时将信息的更新传给currentConditions:
package com.exam.observer;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午6:47:35
*天气信息类
*/
public class WeatherData {
private String temperature;
private String humidity;
private String pressure;
private CurrentConditions current;
public WeatherData(CurrentConditions current){
this.current=current;
}
public String getTemperature() {
return temperature;
}
public String getHumidity() {
return humidity;
}
public String getPressure() {
return pressure;
}
public void setData(String temperature,String humidity,String pressure) {
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
//设置信息时也要调用dataChange()方法。
dataChange();
}
public void dataChange() {
current.update(getTemperature(), getHumidity(), getPressure());
}
}
测试类:
package com.exam.observer;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午6:53:29
*测试类
*/
public class Test {
public static void main(String[] args) {
CurrentConditions current=new CurrentConditions();
WeatherData data=new WeatherData(current);
data.setData("1", "2", "3");
data.setData("4", "5", "6");
}
}
打印结果为:
当前温度为:1
当前湿度为:2
当前大气压为:3
当前温度为:4
当前湿度为:5
当前大气压为:6
此方案实现了基本的功能,气象站(WeatherData)可以发布信息(setData()),然后能够实时的更新信息dataChange()。
但此方案存在一定的问题,分析:
1)其他三方无法进入气象站获取到数据的问题。
2) 无法在运行时动态的添加第三方(新浪)。
3) 违反OCP原则(软件应该对扩展开放,对内修改关闭)--> 观察者模式。
2.改进方案---观察者模式
在普通方案的基础上进行改进,新增subject接口和observer接口,subject接口用来管理多个奶站或者气象站的信息,将信息发布出去。 observer接口则可以用来给多个第三方接入来获取奶站或者气象站的数据。
UML图如下:
奶站或者气象站接口(Subject):
package com.exam.observer.improve;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午7:58:18
*/
public interface Subject {
/**
* 注册observer
*/
public void registerObserver(Observer o);
/**
* 移除observer
*/
public void removeObserver(Observer o);
/**
* 通知所有的observer
*/
public void notifyAllObserver();
}
天气气象站(WeatherData): 管理气象信息和所有的观察者。
package com.exam.observer.improve;
import java.util.ArrayList;
import java.util.List;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午7:51:26
*核心信息,管理观察者
*
*/
public class WeatherData implements Subject{
private String temperature;
private String humidity;
private String pressure;
private List<Observer> observers;
public WeatherData() {
observers=new ArrayList<Observer>();
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
observers.remove(o);
}
public String getTemperature() {
return temperature;
}
public String getHumidity() {
return humidity;
}
public String getPressure() {
return pressure;
}
/**
* 发布信息
*/
public void setData(String temperature,String humidity,String pressure) {
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
update();
}
/**
* 通知观察者修改信息
*/
public void update() {
notifyAllObserver();
}
/**
* 通知所有的观察者
*/
public void notifyAllObserver() {
for(int i=0;i<observers.size();i++) {
observers.get(i).update(getTemperature(), getHumidity(), getPressure());
}
}
}
观察者接口(Observer):
package com.exam.observer.improve;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午7:51:53
*观察者接口,负责接收奶站或者气象站的数据并更新当前拥有的数据。
*/
public interface Observer {
void update(String temperature,String humidity,String pressure);
}
不同的观察者:
package com.exam.observer.improve;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午7:54:33
*/
public class CurrentConditions implements Observer{
private String temperature;
private String humidity;
private String pressure;
public void display() {
System.out.println("当前温度为:"+this.temperature);
System.out.println("当前湿度为:"+this.humidity);
System.out.println("当前大气压为:"+this.pressure);
}
public void update(String temperature, String humidity, String pressure) {
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
}
接入第三方百度:
package com.exam.observer.improve;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午8:13:24
*/
public class Baidu implements Observer{
private String temperature;
private String humidity;
private String pressure;
public void display() {
System.out.println("百度当前温度为:"+this.temperature);
System.out.println("百度当前湿度为:"+this.humidity);
System.out.println("百度当前大气压为:"+this.pressure);
}
public void update(String temperature, String humidity, String pressure) {
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
display();
}
}
测试类:
package com.exam.observer.improve;
/**
*author:bingbing
*日期:2020年5月4日
*时间:下午8:11:12
*/
public class Test {
public static void main(String[] args) {
WeatherData data=new WeatherData();
data.registerObserver(new CurrentConditions());
data.setData("1", "2", "3");
data.registerObserver(new Baidu());
data.setData("3", "4", "5");
}
}
打印结果:
当前温度为:1
当前湿度为:2
当前大气压为:3
当前温度为:3
当前湿度为:4
当前大气压为:5
百度当前温度为:3
百度当前湿度为:4
百度当前大气压为:5
改进的观测者模式的好处:
1.只要气象站的信息有变动,所有的观测者均能够检测到信息的变化,观测者能够获取到第一份数据。
2. 可以横向多个观测者或者气象站,不同的气象站监控不同的区域天气,然后不同的观测监听不同的气象站。提高了程序的扩展性。
三、观测者模式在JDK源码中的应用
Jdk的Observable类就使用了观察者模式
Observable类的源码如下:
package java.util;
/**
* This class represents an observable object, or "data"
* in the model-view paradigm. It can be subclassed to represent an
* object that the application wants to have observed.
* <p>
* An observable object can have one or more observers. An observer
* may be any object that implements interface <tt>Observer</tt>. After an
* observable instance changes, an application calling the
* <code>Observable</code>'s <code>notifyObservers</code> method
* causes all of its observers to be notified of the change by a call
* to their <code>update</code> method.
* <p>
* The order in which notifications will be delivered is unspecified.
* The default implementation provided in the Observable class will
* notify Observers in the order in which they registered interest, but
* subclasses may change this order, use no guaranteed order, deliver
* notifications on separate threads, or may guarantee that their
* subclass follows this order, as they choose.
* <p>
* Note that this notification mechanism has nothing to do with threads
* and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
* mechanism of class <tt>Object</tt>.
* <p>
* When an observable object is newly created, its set of observers is
* empty. Two observers are considered the same if and only if the
* <tt>equals</tt> method returns true for them.
*
* @author Chris Warth
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
* @see java.util.Observer
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
* @since JDK1.0
*/
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector<>();
}
/**
* Adds an observer to the set of observers for this object, provided
* that it is not the same as some observer already in the set.
* The order in which notifications will be delivered to multiple
* observers is not specified. See the class comment.
*
* @param o an observer to be added.
* @throws NullPointerException if the parameter o is null.
*/
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);
}
/**
* Clears the observer list so that this object no longer has any observers.
*/
public synchronized void deleteObservers() {
obs.removeAllElements();
}
/**
* Marks this <tt>Observable</tt> object as having been changed; the
* <tt>hasChanged</tt> method will now return <tt>true</tt>.
*/
protected synchronized void setChanged() {
changed = true;
}
/**
* Indicates that this object has no longer changed, or that it has
* already notified all of its observers of its most recent change,
* so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
* This method is called automatically by the
* <code>notifyObservers</code> methods.
*
* @see java.util.Observable#notifyObservers()
* @see java.util.Observable#notifyObservers(java.lang.Object)
*/
protected synchronized void clearChanged() {
changed = false;
}
/**
* Tests if this object has changed.
*
* @return <code>true</code> if and only if the <code>setChanged</code>
* method has been called more recently than the
* <code>clearChanged</code> method on this object;
* <code>false</code> otherwise.
* @see java.util.Observable#clearChanged()
* @see java.util.Observable#setChanged()
*/
public synchronized boolean hasChanged() {
return changed;
}
/**
* Returns the number of observers of this <tt>Observable</tt> object.
*
* @return the number of observers of this object.
*/
public synchronized int countObservers() {
return obs.size();
}
}
Observer接口:
package java.util;
/**
* A class can implement the <code>Observer</code> interface when it
* wants to be informed of changes in observable objects.
*
* @author Chris Warth
* @see java.util.Observable
* @since JDK1.0
*/
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);
}
四、观察者模式在Spring 中的应用
众所周知,Spring框架在设计时用到了很多设计模式,Spring 通过很多设计模式将代码解耦,也使用到了观察者模式, 例如事件的发布与监听。
Spriing 中的事件流程:
(1) 定义事件类,继承ApplicationEvent。
(2) 定义监听器,实现接口ApplicationListener<? extends ApplicationEvent>, 重写该类下的onApplicationEvent(? event), 在此方法里接收事件发布者发布过来的消息。
(3) 定义事件发布类, 使用applicationContext类调用publishEvent()方法发送事件的内容,即发布消息给监听者。
理解了Spring 发布事件的流程后,就可以基于上述流程实现事件发布与监听。
基于Spring 的event实现事件发布与监听
项目目录
定义事件类
事件类可根据需要继承ApplicationEvent类,因为在监听器中监听到的是ApplicationContext以及子类。
package patternsDesign.observer.application.spring;
import org.springframework.context.ApplicationEvent;
/**
* @author bingbing
* @date 2021/5/23 0023 16:04
*/
public class Event extends ApplicationEvent {
private String eventMessage;
public Event(Object source, String message) {
super(source);
this.eventMessage = message;
}
public String getEventMessage() {
return this.eventMessage;
}
}
定义事件监听者
方式一:
实现ApplicationListener接口,并标记当前类为Spring要管理的Bean。
方式二:
自spring 4.2版本后,可以使用注解形式的监听方式。
@Slf4j
@Component
public class EventListener{
@Async
@EventListener
public void onApplicationEvent(Event event) {
}
注: 开启异步时,需要在主类上开启异步支持@EnableAsync。
监听器一
package patternsDesign.observer.application.spring;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* @author bingbing
* @date 2021/5/23 0023 16:06
*/
@Component
public class EventOneListener implements ApplicationListener<Event> {
@Override
public void onApplicationEvent(Event event) {
String msg = event.getEventMessage();
System.out.println("监听器一收到消息:" + msg);
}
}
监听器二:
package patternsDesign.observer.application.spring;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
* @author bingbing
* @date 2021/5/23 0023 16:06
*/
@Component
public class EventTwoListener implements ApplicationListener<Event> {
@Override
public void onApplicationEvent(Event event) {
String msg = event.getEventMessage();
System.out.println("监听器二收到消息:" + msg);
}
}
注意: 此处需要添加@Component注解,这样才能被Spring 容器扫描到!。
定义事件发布者
使用ApplicationEventPublisher 的publishEvent方法发布事件
package patternsDesign.observer.application.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
/**
* @author bingbing
* @date 2021/5/23 0023 16:01
* 事件发布者
*/
@Component
public class EventPublish {
private final ApplicationContext applicationContext;
@Autowired
public EventPublish(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void publishEvent(String message) {
System.out.println("发布事件...");
applicationContext.publishEvent(new Event(this, message));
}
}
我们定义好发布者与监听者后,最关键的一步,就是需要初始化Spring 容器。
初始化Spring 容器
扫描目录应用所在的目录,使用AnnotationConfigApplicationContext类来扫描包下的所有Spring认识的注解。
package patternsDesign.observer.application.spring;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* @author bingbing
* @date 2021/5/23 0023 16:09
*/
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("patternsDesign.observer.application.spring");
context.start();
// 发布事件
EventPublish publish = new EventPublish(context);
publish.publishEvent("今晚吃龙虾!");
}
}
运行程序, 查看结果:
两个监听者都收到了消息!
基于Guava API轻松实现观察者模式
引入依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
建立监听者, 使用google.guava提供的@subscribe注解标记的方法为被监听的方法:
package patternsDesign.observer.application.guava;
import com.google.common.eventbus.Subscribe;
/**
* @author bingbing
* @date 2021/10/11 0011 11:39
*/
public class GuavaEvent {
@Subscribe
public void subscribe(String str){
// 业务逻辑
System.out.println("执行subscribe方法,入参为:"+str);
}
}
建立发布者, 使用EventBust来注册Event:
package patternsDesign.observer.application.guava;
import com.google.common.eventbus.EventBus;
/**
* @author bingbing
* @date 2021/10/11 0011 11:38
*/
public class GuavaEventTest {
public static void main(String []args){
EventBus eventBus=new EventBus();
GuavaEvent event=new GuavaEvent();
eventBus.register(event);
eventBus.post("Tomy");
}
}
打印结果:
也可以在GuavaEvent 类里添加其他方法,使用@subscribe注解标记,同样发布者在发布事件时,@subscribe标记的方法就会收到。
package patternsDesign.observer.application.guava;
import com.google.common.eventbus.Subscribe;
/**
* @author bingbing
* @date 2021/10/11 0011 11:39
*/
public class GuavaEvent {
@Subscribe
public void subscribe(String str){
// 业务逻辑
System.out.println("执行subscribe方法,入参为:"+str);
}
@Subscribe
public void subscribeTwo(String str){
// 业务逻辑
System.out.println("另外一个方法,执行subscribe方法,入参为:"+str);
}
}
最后
以上就是伶俐草莓为你收集整理的Java设计模式之观察者模式应用与实战一、天气预报项目需求分析二、方案实现三、观测者模式在JDK源码中的应用四、观察者模式在Spring 中的应用基于Guava API轻松实现观察者模式的全部内容,希望文章能够帮你解决Java设计模式之观察者模式应用与实战一、天气预报项目需求分析二、方案实现三、观测者模式在JDK源码中的应用四、观察者模式在Spring 中的应用基于Guava API轻松实现观察者模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复