我是靠谱客的博主 伶俐草莓,最近开发中收集的这篇文章主要介绍Java设计模式之观察者模式应用与实战一、天气预报项目需求分析二、方案实现三、观测者模式在JDK源码中的应用四、观察者模式在Spring 中的应用基于Guava API轻松实现观察者模式,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

一、天气预报项目需求分析

二、方案实现

    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源码中的应用

   JdkObservable类就使用了观察者模式

     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 容器扫描到!。

定义事件发布者

  使用ApplicationEventPublisherpublishEvent方法发布事件

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轻松实现观察者模式所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部