你的位置:首页 > ASP.net教程

[ASP.net教程]设计模式学习-观察者模式


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   @Override100   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   @Override45   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   @Override54   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 into10        * arbitrary code while holding its own Monitor.11        * The code where we extract each Observable from12        * the Vector and store the state of the Observer13        * needs synchronization, but notifying observers14        * does not (should not). The worst result of any15        * potential race-condition here is that:16        * 1) a newly-added Observer will miss a17        *  notification in progress18        * 2) a recently unregistered Observer will be19        *  wrongly notified when it doesn't care20       */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与模式》之观察者模式