java设计模式之观察者模式

Posted 活跃的咸鱼

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java设计模式之观察者模式相关的知识,希望对你有一定的参考价值。

天气预报需求

  1. 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
  2. 需要设计开放型API,便于其他第三方也能接入气象站获取数据。
  3. 提供温度、气压和湿度的接口
  4. 测量数据更新时,要能实时的通知给第三方

普通方案
在这里插入图片描述

  1. 通过getXxx方法,可以让第三方接入,并得到相关信息.
  2. 当数据有更新时,气象站通过调用dataChange() 去更新数据,当第三方再次获取时,就能得到最新数据,当然也可以推送。
public class CurrentConditions {
    private double temperature;//温度
    private double pressure;//气压
    private double humidity;//湿度
    public void updateData(double temperature,double pressure,double humidity){
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        display();
    }

    public void display() {
        System.out.println("today temperature is"+this.temperature);
        System.out.println("today pressure is"+this.pressure);
        System.out.println("today humidity is"+this.humidity);
    }
}

public class WeatherData {
    private double temperature;//温度
    private double pressure;//气压
    private double humidity;//湿度
    private CurrentConditions currentConditions;

    public WeatherData(CurrentConditions currentConditions) {
        this.currentConditions = currentConditions;
    }

    public double getTemperature() {
        return temperature;
    }

    public double getPressure() {
        return pressure;
    }

    public double getHumidity() {
        return humidity;
    }
    public  void dataChange(){
     currentConditions.updateData(getTemperature(),getPressure(),getHumidity());
    }
    public void setData(double temperature,double pressure,double humidity){
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        dataChange();
    }
}
public class Client {
    public static void main(String[] args) {
        CurrentConditions currentConditions = new CurrentConditions();
        WeatherData weatherData = new WeatherData(currentConditions);
        weatherData.setData(38,90,80);
    }
}

today temperature is38.0
today pressure is90.0
today humidity is80.0

问题分析

  1. 其他第三方接入气象站获取数据的问题
    //在WeatherData中,当增加一个第三方,都需要创建一个对应的第三方的公告板对象,并加入到dataChange, 不利于维护,也不是动态加入
  2. 无法在运行时动态的添加第三方 (新浪网站)
  3. 违反ocp原则=>观察者模式

观察者模式

观察者模式介绍

概念其实很简单,两个主体,一个观察者,一个被观察者,当被观察者发生变化时,观察者会有相应的动作。举几个例子,和我们日常生活息息相关的红绿灯,灯就相当于被观察者,行人就相当于观察者,当灯发生变化时,行人会有相应的动作:红灯停,绿灯行,黄灯亮了等一等。再比如我们现在玩的公众号,当我们订阅了某个公众号之后,公众号每发表一篇文章,就会向订阅了它的用户发送这篇文章,我们就可以浏览这篇文章了;当我们取消订阅了,它就不会再向我们推送这篇文章了;只要这个公众号一直在运行,就会一直有人订阅它或者取消订阅。这两个主体有个统一的称呼:被观察者成为主题(Subject),观察者仍是称为观察者(Observer)。

观察者模式还有很多其他的称谓,如发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

观察者模式原理
在这里插入图片描述
观察者模式类似上诉天气预报业务

  1. 气象局:Subject
  2. 用户/第三方网站:Observer

Subject:登记注册、移除和通知

  1. registerObserver 注册
  2. removeObserver 移除
  3. notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实时推送,看具体需求定

原理类图
在这里插入图片描述
角色分析

抽象主题(Subject):提供接口,可以增加和剔除观察者对象。一般用抽象类或者接口实现。

抽象观察者(Observer):提供接口,在得到主题的通知时更新自己。一般用抽象类或者接口实现。

具体主题(ConcreteSubject):将有关状态存入具体观察者,在具体主题的内部状态发生变化时,给所有注册过的观察者发出通知。一般是具体子类实现。

具体观察者(ConcreteObserver):存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用

在上述类图中,ConcreteSubject中有一个存储Observer的列表,这意味着ConcreteSubject并不需要知道引用了哪些ConcreteObserver,只要实现(继承)了Observer的对象都可以存到该列表中。在需要的时候调用Observer的update方法。

观察者模式解决天气预报问题
类图:
在这里插入图片描述
Subject

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

Observer

public abstract class Observer {
    protected double temperature;//温度
    protected double pressure;//气压
    protected double humidity;//湿度
    public abstract  void update(double temperature,double pressure,double humidity);
    public abstract void display();

    public double getTemperature() {
        return temperature;
    }

    public void setTemperature(double temperature) {
        this.temperature = temperature;
    }

    public double getPressure() {
        return pressure;
    }

    public void setPressure(double pressure) {
        this.pressure = pressure;
    }

    public double getHumidity() {
        return humidity;
    }

    public void setHumidity(double humidity) {
        this.humidity = humidity;
    }
}

ConcreteSubject

public class WeatherDatas implements Subject{
    private double temperature;//温度
    private double pressure;//气压
    private double humidity;//湿度
    private List<Observer> observerList;

    public WeatherDatas() {
        this.observerList=new ArrayList<>();
    }

    public double getTemperature() {
        return temperature;
    }

    public double getPressure() {
        return pressure;
    }

    public double getHumidity() {
        return humidity;
    }
    public void setData(double temperature,double pressure,double humidity){
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        dataChange();
    }

    public void dataChange() {
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        observerList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observerList.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (int i = 0; i <observerList.size() ; i++) {
            observerList.get(i).update(getTemperature(),getPressure(),getHumidity());
        }

    }
}

ConcreteObserver

public class weatherBureau extends Observer{

    @Override
    public void update(double temperature, double pressure, double humidity) {
        setHumidity(humidity);
        setPressure(pressure);
        setTemperature(temperature);
        display();
    }

    @Override
    public void display() {
        System.out.println("today temperature is "+getTemperature());
        System.out.println("today pressure is "+getPressure());
        System.out.println("today humidity is "+getHumidity());
    }
}


public class BaiduWeather extends Observer{

    @Override
    public void update(double temperature, double pressure, double humidity) {
        setHumidity(humidity);
        setPressure(pressure);
        setTemperature(temperature);
        display();
    }

    @Override
    public void display() {
        System.out.println("baidu prompt today temperature is "+getTemperature());
        System.out.println("baidu prompt today pressure is "+getPressure());
        System.out.println("baidu prompt today humidity is "+getHumidity());
    }
}
public class Tests {
    public static void main(String[] args) {
        WeatherDatas weatherDatas = new WeatherDatas();
        weatherBureau weatherBureau = new weatherBureau();
        BaiduWeather baiduWeather = new BaiduWeather();
        weatherDatas.registerObserver(weatherBureau);
        weatherDatas.registerObserver(baiduWeather);
        weatherDatas.setData(34,78,97);
    }
}

today temperature is 34.0
today pressure is 78.0
today humidity is 97.0
baidu prompt today temperature is 34.0
baidu prompt today pressure is 78.0
baidu prompt today humidity is 97.0

jdk实现

在Java语言的java.util包下,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。
类图如下:
在这里插入图片描述

Observable:

package java.util;

/**
 * 抽象主题,用普通类实现
 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    
    /**
     * 构建一个含有0个观察者的主题
     */
    public Observable() {
        obs = new Vector<>();
    }

    /**
     * 注册某个观察者到obs中
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * 从obs中移除某个观察者
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * 相当于notifyObservers(null),具体看下面那个
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * 如果本对象有变化,则通知所有注册了的观察者,调用他们的update方法
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * 清空obs
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * 将changed设置成true,标明本对象发生了变化
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * 将changed重置成false
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * 检测本对象是否发生了变化
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * 返回注册的观察者数量
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

Observer:

package java.util;

/**
 * 抽象观察者,接口实现
 */
public interface Observer {
    /**
     * 当被观察者对象发生改变时,此方法被调用
     */
    void update(Observable o, Object arg);
}

Watched:

package com.lee.jdkobserver;

import java.util.Observable;

/**
 * 具体主题
 */
public class Watched extends Observable {
    private String data = "";

    public void changeData(String data) {
        if (this.data.equals(data)) {
            return;
        }
        this.data = data;
        setChanged();
        notifyObservers(this.data);
    }
}

Watcher:

package com.lee.jdkobserver;

import java.util.Observable;
import java.util.Observer;

/**
 * 具体观察者,实现jdk中的Observer
 */
public class Watcher implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("数据改变成了:" + arg);
    }
}

JdkObserverTest:

package com.lee.test;

import com.lee.jdkobserver.Watched;
import com.lee.jdkobserver.Watcher;

import java.util.Observer;

public class JdkObserverTest {

    public static void main(String[] args) {
        Watched watched = new Watched();
        Observer observer = new Watcher();
        watched.addObserver(observer);
        watched.changeData("first");
        watched.changeData("second");
        watched.changeData("third");
        watched.changeData("fourth");
    }
}

在这里插入图片描述

jdk事件

JDK 1.0及更早版本的事件模型基于职责链模式,但是这种模型不适用于复杂的系统,因此在JDK 1.1及以后的各个版本中,事件处理模型采用基于观察者模式的委派事件模型(DelegationEvent Model, DEM),即一个Java组件所引发的事件并不由引发事件的对象自己来负责处理,而是委派给独立的事件处理对象负责。这并不是说事件模型是基于Observer和Observable的,事件模型与Observer和Observable没有任何关系,Observer和Observable只是观察者模式的一种实现而已。

java中的事件机制的参与者有3种角色

Event Eource:事件源,发起事件的主体。
Event Object:事件状态对象,传递的信息载体,就好比Watcher的update方法的参数,可以是事件源本身,一般作为参数存在于listerner的方法之中。
Event Listener:事件监听器,当它监听到event object产生的时候,它就调用相应的方法,进行处理。

其实还有个东西比较重要:事件环境,在这个环境中,可以添加事件监听器,可以产生事件,可以触发事件监听器。

spring事件机制

spring的事件机制也是从java的事件机制拓展而来,具体往下看

ApplicationEvent:Spring中所有的事件父接口,继承自java的EventObject

package org.springframework.context;

import java.util.EventObject;

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
public abstract class ApplicationEvent extends EventObject {

    /** use serialVersionUID from Spring 1.2 for interoperability */
    private static final long serialVersionUID = 7099057708183571937L;

  

以上是关于java设计模式之观察者模式的主要内容,如果未能解决你的问题,请参考以下文章

JAVA SCRIPT设计模式--行为型--设计模式之Observer观察者模式(19)

java之观察者模式

设计模式之观察者模式与访问者模式详解和应用

Java观察者模式之总有你想不到的知识

java 设计模式之 观察者模式(Observer)

java设计模式之观察者模式以及在java中作用