zoukankan      html  css  js  c++  java
  • 设计模式学习-观察者模式

    1.定义

    定义对象间一种一对多的依赖关系,使得当每一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

    2.类图

    3.代码示例

    我们定义一个场景:热水壶在烧开水,小孩和妈妈都关注烧开水的过程,各自有其处理方法。用while死循环一直轮询虽然可以实现这样的场景,但性能上让人无法接受。

    为方便大家copy源码放在本机测试,我将代码写进一个java类中,其它参与类都是非public的。

      1 package com.zhaoyangwoo.observer;
      2 
      3 import java.util.ArrayList;
      4 import java.util.List;
      5 
      6 /**
      7  * Created by john on 16/8/3.
      8  */
      9 //定义被观察者接口
     10 interface Subject {
     11 
     12     void attachObv(Observer o);
     13 
     14     void detachObv(Observer o);
     15 
     16     void notifyObvs();
     17 }
     18 
     19 //定义观察者接口
     20 interface Observer {
     21     void update(Subject subject);
     22 }
     23 
     24 //定义具体被观察者
     25 public class WaterHeater implements Subject {
     26 
     27     //存储观察者
     28     private static List<Observer> observerList = new ArrayList<>();
     29 
     30     public int getTemperature() {
     31         return temperature;
     32     }
     33 
     34     public void setTemperature(int temperature) {
     35         this.temperature = temperature;
     36     }
     37 
     38     private int temperature = 0;
     39 
     40     @Override
     41     public void attachObv(Observer o) {
     42         observerList.add(o);
     43     }
     44 
     45     @Override
     46     public void detachObv(Observer o) {
     47         observerList.remove(o);
     48 
     49     }
     50 
     51     @Override
     52     public void notifyObvs() {
     53         observerList.forEach(r -> r.update(this));
     54     }
     55 
     56     //模拟烧开水的过程
     57     public void heat() {
     58         temperature = 0;
     59         for (int i = 0; i < 100; i++) {
     60             temperature++;
     61             this.notifyObvs();
     62             try {
     63                 //停100ms
     64                 Thread.sleep(100);
     65             } catch (InterruptedException e) {
     66                 e.printStackTrace();
     67             }
     68         }
     69     }
     70 
     71 
     72     //场景类
     73     public static void main(String[] args) {
     74         WaterHeater waterHeater = new WaterHeater();
     75         Mother mother = new Mother();
     76         Child child = new Child();
     77         waterHeater.attachObv(mother);
     78         waterHeater.attachObv(child);
     79         waterHeater.heat();
     80         waterHeater.detachObv(child);
     81         waterHeater.heat();
     82         System.out.println("烧水结束");
     83     }
     84 }
     85 
     86 //具体观察者
     87 class Child implements Observer {
     88 
     89     @Override
     90     public void update(Subject subject) {
     91         WaterHeater waterHeater = (WaterHeater) subject;
     92         System.out.println("现在的水温是" + waterHeater.getTemperature() + "度,人家还是个宝宝,跟我没关系");
     93     }
     94 }
     95 
     96 //具体观察者
     97 class Mother implements Observer {
     98 
     99     @Override
    100     public void update(Subject subject) {
    101         WaterHeater waterHeater = (WaterHeater) subject;
    102         if (waterHeater.getTemperature() > 99) {
    103             System.out.println("现在的水温是" + waterHeater.getTemperature() + "度,水烧开了,我要把水装进热水瓶");
    104         }
    105     }
    106 }

    4.应用场景举例

    • 一个类的状态变化需要通知其他类知晓,并且这些其他类是可以动态配置的
    • 可以实现订阅/发布模型

    5.JDK源码中的模式实现

    JDK原生对观察者模式支持。通过java.util.Observable和java.util.Observer两个类,很容易实现观察者模式。通过阅读其源码,可以发现原理都是一样的。当然源码里的实现是线程安全的。我们用这两个类重写我们的场景:

     1 // jdk版本的被观察者,不需要自己实现调用通知/注册之类的操作
     2 class WaterHeaterJava extends Observable {
     3     public int getTemperature() {
     4         return temperature;
     5     }
     6 
     7     public void setTemperature(int temperature) {
     8         this.temperature = temperature;
     9     }
    10 
    11     private int temperature = 0;
    12 
    13     public void heat() {
    14         temperature = 0;
    15         for (int i = 0; i < 100; i++) {
    16             temperature++;
    17             //这里一定要注意,如果要notifyObservers生效,一定要调用setChanged告知已经发生了change,可以通知观察者了.否则notifyObservers不工作
    18             super.setChanged();
    19             super.notifyObservers();
    20             try {
    21                 Thread.sleep(100);
    22             } catch (InterruptedException e) {
    23                 e.printStackTrace();
    24             }
    25         }
    26     }
    27 
    28     //场景类
    29     public static void main(String[] args) {
    30         WaterHeaterJava waterHeater = new WaterHeaterJava();
    31         MotherJava mother = new MotherJava();
    32         ChildJava child = new ChildJava();
    33         waterHeater.addObserver(mother);
    34         waterHeater.addObserver(child);
    35         waterHeater.heat();
    36         waterHeater.deleteObserver(child);
    37         waterHeater.heat();
    38         System.out.println("Java版烧水结束");
    39     }
    40 }
    41 
    42 class ChildJava implements java.util.Observer {
    43 
    44     @Override
    45     public void update(Observable o, Object arg) {
    46         WaterHeaterJava waterHeater = (WaterHeaterJava) o;
    47         System.out.println("现在的水温是" + waterHeater.getTemperature() + "度,人家还是个宝宝,跟我没关系");
    48     }
    49 }
    50 
    51 class MotherJava implements java.util.Observer {
    52 
    53     @Override
    54     public void update(Observable o, Object arg) {
    55         WaterHeaterJava waterHeater = (WaterHeaterJava) o;
    56         if (waterHeater.getTemperature() > 99) {
    57             System.out.println("现在的水温是" + waterHeater.getTemperature() + "度,水烧开了,我要把水装进热水瓶");
    58         }
    59     }
    60 }
    View Code

    关于setChanged,看看它的源码就明白了

     1 public void notifyObservers(Object arg) {
     2         /*
     3          * a temporary array buffer, used as a snapshot of the state of
     4          * current Observers.
     5          */
     6         Object[] arrLocal;
     7 
     8         synchronized (this) {
     9             /* We don't want the Observer doing callbacks into
    10              * arbitrary code while holding its own Monitor.
    11              * The code where we extract each Observable from
    12              * the Vector and store the state of the Observer
    13              * needs synchronization, but notifying observers
    14              * does not (should not).  The worst result of any
    15              * potential race-condition here is that:
    16              * 1) a newly-added Observer will miss a
    17              *   notification in progress
    18              * 2) a recently unregistered Observer will be
    19              *   wrongly notified when it doesn't care
    20              */
    21 
    22             //看这里
    23             if (!changed)
    24                 return;
    25             arrLocal = obs.toArray();
    26             clearChanged();
    27         }
    28 
    29         for (int i = arrLocal.length-1; i>=0; i--)
    30             ((Observer)arrLocal[i]).update(this, arg);
    31     }    
    View Code

     

    6.思考

    思考如下两个问题

    • 性能问题 

      如果注册的观察者较多,或者观察者处理能力弱、耗时长,那么很可能出现性能问题,毕竟只是简单的遍历调用。

    • 订阅/发布

      当然,对于订阅/发布模型的支持有更好的开源框架,各种mq实现了这种模型,并且是异步架构,性能也有保障。 

    • 推或拉

      大家都喜欢说推模式和拉模式。其实本质上来说都是推模式,这也是使用观察者模式带来的好处。拉模式只不过推了一个引用,可以通过这个引用拿到更多的信息而已。

    7.参考

    1.《JAVA与模式》之观察者模式
     

    作者: zhaoyanghoo
    出处: http://www.cnblogs.com/zhaoyanghoo/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    链表重排 【模板题】
    链表的插入排序
    链表归并排序
    判断链表成环| 删除第K个节点【快慢指针】
    vue骨架屏制作
    前端常用的网站+插件
    点击canvas图片下载图片
    判断dom是否出现在可视区域
    canvas截取图片
    .如果在input上加上@keyup.enter.native,第一次回车时还是会刷新界面,在el-from上加上 @submit.native.prevent
  • 原文地址:https://www.cnblogs.com/zhaoyanghoo/p/5734776.html
Copyright © 2011-2022 走看看