基本需求:

  • 气象站可以将每天测量到的温度,湿度,气压等等,以公告的形式发布出去(比如发布到自己的网站或第三方)
  • 需要设计开放型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设计模式之观察者模式的更多相关文章

  1. 理解java设计模式之观察者模式

    在生活实际中,我们经常会遇到关注一个事物数据变化的情况,例如生活中的温度记录仪,当温度变化时,我们观察它温度变化的曲线,温度记录日志等.对于这一类问题,很接近java设计模式里面的“观察者模式”,它适 ...

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

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...

  3. java设计模式02观察者模式

    观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 这里主要讲一下学习内置观察者的记录,在JA ...

  4. java设计模式之观察者模式

    观察者模式 观察者模式(有时又被称为发布(publish )-订阅(Subscribe)模式.模型-视图(View)模式.源-收听者(Listener)模式或从属者模式)是软件设计模式的一种.在此种模 ...

  5. JAVA设计模式 之 观察者模式

    简介: 观察者模式是JDK中最多的设计模式之一,非常有用,观察者模式介绍了一对多的依赖关系及松耦合,有了观察者,你将会消息灵通. 认识观察者模式,看一个报纸.杂志订阅是怎么回事: (1). 报社的业务 ...

  6. 折腾Java设计模式之观察者模式

    观察者模式 Define a one-to-many dependency between objects where a state change in one object results in ...

  7. java设计模式之-观察者模式(发布-订阅模式)

    1.观察者模式定义  观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态上发生变化时,会通知所有观察者对象,让它们能够自动更新自己. 2.观察者模式结构 ...

  8. JAVA设计模式之观察者模式 - Observer

    有趣的事情发生时,可千万别错过了!有一个模式可以帮你的对象知悉现况,不会错过该对象感兴趣的事.对象甚至在运行时可决定是否要继续被通知.有了观察者,你将会消息灵通. 介绍 观察者模式的定义: 在对象之间 ...

  9. JAVA设计模式 之 观察者模式(JDK内置实现)

    简介:使用JAVA内置的帮你搞定观察者模式. 1. 先把类图放在这里: (1). Observable类追踪所有的观察者,并通知他们. (2). Observer这个接口看起来很熟悉,它和我们之前写的 ...

随机推荐

  1. 水题挑战4: luogu P1280 尼克的任务

    题目描述 尼克每天上班之前都连接上英特网,接收他的上司发来的邮件,这些邮件包含了尼克主管的部门当天要完成的全部任务,每个任务由一个开始时刻与一个持续时间构成. 尼克的一个工作日为 \(n\) 分钟,从 ...

  2. 通过阿里镜像网站制作iso文件安装CentOS6.9

    基于网络安装 创建kickstart文件的方式: 1.复制模板/root/anaconda-ks.cfg,而后使用vim编辑配置 2.使用system-config-kickstart来生成,建议使用 ...

  3. (3)ASP.NET Core3.1 Ocelot认证

    1.认证 当客户端通过Ocelot访问下游服务的时候,为了保护下游资源服务器会进行认证鉴权,这时候需要在Ocelot添加认证服务.添加认证服务后,随后使用Ocelot基于声明的任何功能,例如授权或使用 ...

  4. 完美实现CSS垂直居中的11种方法

    在做前端项目时CSS的水平居中我们经常使用,但有时还会用到CSS垂直居中,对于小白来说这个就有些难度了,下面看一下我是如何实现的 本人前端小白,正在做一个小程序开发的项目,css样式调整搞的头都大了. ...

  5. ORA-00020: maximum number of processes (40) exceeded模拟会话连接数满

    问题描述:在正式生产环境中,有的库建的process和session连接数目设置的较小,导致后期满了无法连接.因为正式库无法进行停库修改,只能释放连接,做个测试模拟 1. 修改现有最大会话与进程连接数 ...

  6. 聊一聊无锁队列rte_ring

    之前用基于dpdk 实现小包快速转发的时候有用到无锁队列!今天就来看看吧!(后续完成了去dpdk化,直接在内核完成快速转发功能) dpdk的无锁队列ring是借鉴了linux内核kfifo无锁队列.r ...

  7. 1、线性DP 198. 打家劫舍

    198. 打家劫舍 https://leetcode-cn.com/problems/house-robber/ //dp动态规划,dp[i] 状态表示0-i家的盗的得最大值.那么dp[i] = (d ...

  8. 分库分表神器 Sharding-JDBC,几千万的数据你不搞一下?

    今天我们介绍一下 Sharding-JDBC框架和快速的搭建一个分库分表案例,为讲解后续功能点准备好环境. 一.Sharding-JDBC 简介 Sharding-JDBC 最早是当当网内部使用的一款 ...

  9. Linux操作系统选择

    主流的操作系统 ubuntu centos debian oracle linux 主要使用的操作系统就是上面几个,主要是ubuntu和centos,debian是基于ubuntu改的,oracle ...

  10. HotSpot类模型之ArrayKlass

    上一篇分析了 HotSpot类模型之InstanceKlass ,这次主要分析表示java数组类型的C++类. 1.ArrayKlass类 ArrayKlass继承自Klass,是所有数组类的抽象基类 ...