当前位置:   article > 正文

设计模式:观察者模式

观察者模式

1.1 观察者模式

1.1.1 观察者模式的引入案例

天气预报在现实生活中的有着非常广泛的应用,在即将发生灾害天气时,专用的气象广播电台可用一定波长的信号,使这种收音机自动开启呼叫,这样,入睡的人也能被其信号唤醒,收听到灾害性天气警报,这对及时采取预防措施提供了可能性。

当我们在实现一个气象信息应用的时候,气象站实时监测并更新天气信息,与此同时终端各种用户界面要实时显示最新的天气信息,具体要求如下:

  1. 气象站可以将每天测到的温度、湿度、气压、PM2.5等以公告的形式发布到自己的网站或第三方。

  2. 有对外的接口可以被其他系统所调用,当气象站数据更新后,天气接口程序去获取最新天气数据,然后将数据分发给所有订阅过“天气日报”程序的用户,及时更新数据。

查看大图

1.1.2 观察者模式动机分析

观察者模式的模式动机是建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

1.1.3 观察者模式定义

观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖的对象皆得到通知并被自动更新。观察者模式又叫发布-订阅(Publish/Subscribe)模式、模式-视图(Model/View)模式、源-监听器(Source/Listener)模式或丛书者(Dependents)模式。

观察者模式是一种对象行为型模式。

Subject:抽象主题(被观察者),每一个主题可以有多个观察者,并将所有观察者对象的引用保存在一个集合里,被观察者提供一个接口,可以增加和删除观察者角色。

ConcreteSubject:具体主题,将有关状态存入具体观察者对象,在主题发生改变时,给所有的观察者发出通知。

Observer:抽象观察者,为所有的具体观察者定义一个更新接口,该接口的作用是在收到主题的通知时能够及时的更新自己。

ConcreteObserver:具体观察者,实现抽象观察者角色定义的更新接口,以便使本身的状态与主题状态相协调。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

1.1.4 观察者模式类图

image-20220516154822561

1.1.5 观察者模式类图对应的源码框架

1.1.5.1.定义观察者接口

public interface Observer {
    void response();
}

  • 1
  • 2
  • 3
  • 4

1.1.5.2.定义被观察者接口

public abstract class Subject {

    protected Vector<Observer> observers;

    public void add(Observer observer){
        if(observers == null){
            observers = new Vector<>();
        }
        this.observers.add(observer);
    }

    public void removeObserver(Observer observer){
        observers.remove(observer);
    }

    public abstract void notifyObserver();

    public abstract void doSomething();
    
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

1.1.5.3.定义具体的被观察者

public class ConcreteSubject extends Subject {
    @Override
    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.response();
        }
    }

    @Override
    public void doSomething() {
        System.out.println("被观察者事件发生改变");
        notifyObserver();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

1.1.5.4.定义具体的观察者

public class ConcreteObserver1 implements Observer {

    @Override
    public void response() {
        System.out.println("观察者1收到信息...");
    }
}

public class ConcreteObserver2 implements Observer {
    @Override
    public void response() {
        System.out.println("观察者2收到信息...");
    }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

1.1.5.5.定义客户端

public class Client {
    public static void main(String[] args) {
        Subject sub = new ConcreteSubject();
        sub.add(new ConcreteObserver1());
        sub.add(new ConcreteObserver2());
        sub.doSomething();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.1.5.6.运行测试

image-20220516155542063

1.1.6 观察者模式案例及对应的源码(可运行)

当我们在实现一个气象信息应用的时候,气象站实时监测并更新天气信息,与此同时终端各种用户界面要实时显示最新的天气信息,具体要求如下:

1.气象站可以将每天测到的温度、湿度、气压、PM2.5等以公告的形式发布到自己的网站或第三方。

2.有对外的接口可以被其他系统所调用,当气象站数据更新后,天气接口程序去获取最新天气数据,然后将数据分发给所有订阅过“天气日报”程序的用户,及时更新数据。

image-20220516155529553

1.1.6.1抽象目标类

public abstract class Subject {

    protected Vector<Observer> observers;

    public abstract void addObserver(Observer observer);

    public abstract void removeObserver(Observer observer);

    public abstract void notifyObserver();

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

1.1.6.2.观察者类

public interface Observer {
    void response();
}

  • 1
  • 2
  • 3
  • 4

1.1.6.3.气象台类

public class WeatherData extends Subject{

    private float temperature;
    private float pressure;
    private float humidity;


    @Override
    public void addObserver(Observer observer) {
        if(observers == null){
            observers = new Vector<>();
        }
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if(i >= 0){
            observers.remove(i);
        }
    }



    @Override
    public void notifyObserver() {
        for (Observer observer : observers) {
            observer.response();
        }
    }

    public void measurementChanged(){
        notifyObserver();
    }

    public void setMeasurements(float temperature,float pressure,float humidity){
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        System.out.println("当前状况:温度->"+this.temperature+"华氏度,气压->"+this.pressure+",湿度->"+this.humidity);
        measurementChanged();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

1.1.6.4.用户类

public class User implements Observer {

    private String name;

    @Override
    public void response() {
        System.out.println("用户:"+name+" 接收到气象站更新的数据。");
    }

    public User(String name) {
        this.name = name;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

1.1.6.5.客户测试类

public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        User user1 = new User("张三");
        User user2 = new User("李四");
        User user3 = new User("王五");

        weatherData.addObserver(user1);  //张三订阅
        weatherData.addObserver(user2);  //李四订阅

        weatherData.setMeasurements(80,  30.4f,65);
        weatherData.setMeasurements(82,  29.2f,70);
        weatherData.setMeasurements(78,  40.4f,78);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

1.1.6.6.运行截图

image-20220516155511567

1.1.7 观察者模式在JDK、Spring、SpringMVC、Springboot或者Android框架源码任何第三方应用的类图及源码分析。

观察者模式在JDK、Spring、Springboot中都有着广泛的应用,如JDK的事件机制、Spring及Springboot的事件监听机制,下面就Spring中的观察者模式展开分析。

1.1.7.1 观察者模式在Spring中的源码分析

Spring中的事件监听机制,很多人也叫做监听器模式,其实是在观察者模式中对观察者模式实现了更加的抽象化和细化,其中主要有四个角色:

ApplicationListener:事件监听器,等同于观察者Observer,实现类继承该接口,重写onApplicationEvent方法,处理对事件发生的响应。

ApplicationEvent:事件,是所有事件对象的父类,ApplicationEvent继承自jdk的EventObject,所有的事件继承ApplicationEvent,监听器监听到事件后作出响应,spring这里进行了抽象,抽象出了这个具体的接口,运用起来更加灵活,扩展性更强。

ApplicationEventMulticaster:事件多路广播器,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把Applicationcontext发布的Event广播给他的监听器列表,下图是ApplicationEventMulticaster的类结构,与观察者模式的抽象目标相似。

image-20220516155458624

ApplicationEventPublisher:事件发布者,即事件源通过该接口的publishEvent方法发布时间,所有的应用上下文都具备事件发布能力,ApplicationContext实现了该接口。

image-20220516155449046

Spring****事件监听机制流程:

image-20220516155408548

1.流程分为两个阶段

​ (1)一个是启动Spring容器

​ (2)另外一个是我们触发事件的时候

2.核心还是事件广播器ApplicationEventMulticaster(这里实际指的是它的实现类ApplicationEventMulticaster,SimpleApplicationEventMulticaster)

3.增加监听器是在启动Spring容器时候完成的(图中紫红色的部分)。这也是Spring容器的核心位置。这两个addxxx分别是:

(1)增加普通的监听器,即通过实现ApplicationListener实现的监听器

(2)增加使用注解(@EventListener)实现的监听器

4.事件发布。这是我们写程序可触及到的一部分流程。核心是ApplicationEventPublisher。这里会首先去调用事件广播器的getApplicationListeners方法,拿到所有的监听器(由于前面启动时已经加载里所有监听器,所以这里可以拿到),然后逐个调用监听器内的方法。

总结

1.从AbstractApplicationContext类中的Set<ApplicationListener<?>> applicationListeners取出用户添加的监听器ApplicationListener,添加到多路广播器中

2.从spring工厂中取出符合条件的ApplicationListener,添加到多路广播器中

3.对早期添加在earlyApplicationEvents中的事件进行发布

1.1.7.2 观察者模式在Spring中事件监听机制中的类图

image-20220516155323094

1.1.8 观察者模式扩展分析

1.1.8.1 观察者模式的两种实现方式

(1)拉取模式:目标角色在发生变化后,仅仅告诉观察者角色“我变化了”,观察者角色如果想要知道具体的变化细节,则就要自己从目标角色的接口中得到,这种模式称为拉取模式,就是说变化的信息是观察者角色主动从目标角中“拉”出来的。

(2)推送模式:目标角色发生变化时,通知观察者的同时,通过参数将变化的细节传递到观察者角色中去。

这两种模式的使用取决于系统设计,如果目标角色比较复杂,并且观察者角色进行更新时必须得到一些具体变化的信息,则“推模式”比较合适,如果目标角色比较简单,则“拉模式”就很合适啦。

1.1.8.2 观察者模式与发布订阅模式的区别

发布与订阅模式和观察者模式有以下不同:

  1. 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中, 生产者与消费者不知道对方的存在,它们之间通过频道进行通信。

  2. 观察者模式是同步的,当事件触发时,主题会调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,生产者向频道发送一个消息之后, 就不需要关心消费者何时去订阅这个消息,可以立即返回。

img

1.1.9 观察者模式优缺点分析

优点:一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需要增加和删除观察者,使得系统更易于扩展,符合开闭原则;并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但依然可以交互,目标对象只知道一个具体的观察者列表,并不认识一个具体的观察者,它只知道他们都有一个共同的接口。

观察者模式支持广播通信。

观察者模式符合“开闭原则”的要求。

缺点:观察者模式的缺点在于如果存在很多个被观察者的话,那么将需要花费一定时间通知所有的观察者,如果观察者与被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有对应的机制让观察者知道被观察者对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/286266?site
推荐阅读
相关标签
  

闽ICP备14008679号