• headFirst设计模式——观察者模式


    一、介绍

      观察者(Observer)模式可以帮你的对象知悉现况,不会错过该对象感兴趣的事。

      对象甚至在运行时可决定是否要继续被通知。以及一对多关系,和松耦合。

      有了观察者,你将会消息灵通。

      观察者模式定义了对象之间的一对多依赖,这样一来当一个对象改变状态时,

      它的所有依赖着都会收到通知并自动更新。

      实现观察者模式的方法不止一种,但是以包含Subject和Observer接口的类设计的做法最常见。

    二、引入

      恭喜贵公司获选为敝公司建立下一代Internet气象观测站!

      该气象站必须建立在我们专利申请中的WeatherData对象
      上,由WeatherData对象负责追踪目前的天气状况(温度、
      湿度、气压)。我们希望贵公司能建立一个应用,有三种
      布告板,分别显示目前的状况、气象统计及简单的预报。
      当WeatherObject对象获得最新的测量数据时,三种布告板
      必须实时更新。
      而且,这是一个可以扩展的气象站,Weather-O-Rama气象
      站希望公布一组API,好让其他开发人员可以写出自己的
      气象布告板,并插入此应用中。我们希望贵公司能提供这
      样的API。
      Weather-O-Rama气象站有很好的商业营运模式:一旦客
      户上钩,他们使用每个布告板都要付钱。最好的部分就是,
      为了感谢贵公司建立此系统,我们将以公司的认股权支付
      你。
      我们期待看到你的设计和应用的alpha版本。
      
      WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。
      我们的工作就是建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。
      

       

      
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

      报纸的订阅 出版者+订阅者=观察者模式
      出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observe)。
      
     
     
     
    设计原则:
      1、为了交互对象之间的松耦合设计而努力。
      松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低。
     
     三、实现
      
      Java为观察者模式提供了内置支持,但是许多时候,自己建立这一切会更具弹性。
      主题
    1 public interface Subject {
    2 
    3     public void registerObserver(Observer o);
    4     
    5     public void removeObserver(Observer o);
    6     
    7     public void notifyObservers();
    8 }

      主题实现类

     1 public class WeatherData implements Subject{
     2     private ArrayList<Observer> observers;
     3     private float temperature;
     4     private float humidity;
     5     private float pressure;
     6 
     7 //        我们加上一个ArrayList来纪录观察者,此ArrayList是在构造器中建立的。
     8     public WeatherData(){
     9         observers = new ArrayList<Observer>();
    10     }
    11     
    12     @Override
    13     public void registerObserver(Observer o) {
    14         observers.add(o);
    15     }
    16 
    17     @Override
    18     public void removeObserver(Observer o) {
    19         int i = observers.indexOf(o);
    20         if (i >= 0){
    21             observers.remove(i);
    22         }
    23     }
    24 
    25     @Override
    26     public void notifyObservers() {
    27         // 在这里,我们把状态告诉每一个观察者。因为观察者都实现了update(),所以我们知道如何通知它们。
    28         for (int i = 0; i < observers.size(); i++) {
    29             Observer observer = observers.get(i);
    30             observer.update(temperature, humidity, pressure);
    31         }
    32     }
    33 
    34     public void measurementsChanged(){
    35         // 当从气象站得到更新观测值 时,我们通知观察者。 
    36         notifyObservers();
    37     }
    38     
    39     public void setMeasurements(float temperature, float humidity, float pressure) {//模拟从气象站收到数据
    40          this.temperature = temperature;
    41          this.humidity = humidity;
    42          this.pressure = pressure;
    43          measurementsChanged();
    44     }
    45     
    46 }

      观察者

    1 public interface Observer {
    2 
    3 //    暗示:这些观测值的种类和个数在未来有可能改变吗?如果以后会改变,这些变化是否被很好地封装?或者是需要修改许多代码才能办到?
    4     public void update(float temp, float humidity, float pressure);
    5 }

      每个观察者必须实现的接口

    1 public interface DisplayElement {
    2 
    3     public void display();
    4 }

      观察者实现类

     1 public class CurrentConditionsDisplay implements DisplayElement, Observer {
     2     private float temperature;
     3     private float humidity;
     4     private Subject weatherData;
     5 
     6     // 构造器需要 weatherData对象(也就是主题)作为注册用。
     7     public CurrentConditionsDisplay(Subject weatherData) {
     8         this.weatherData = weatherData;
     9         weatherData.registerObserver(this);
    10     }
    11 
    12     @Override
    13     public void update(float temperature, float humidity, float pressure) {
    14         this.temperature = temperature;
    15         this.humidity = humidity;
    16         display();                    // 这里并不是调用display()最合适的地方。
    17     }
    18 
    19     @Override
    20     public void display() {
    21         System.out.println("Current conditions: "+ temperature +
    22             "F degrees and " + humidity +"%humidity");
    23     }
    24 
    25 }

      测试

     1 public class WeatherStation {
     2 
     3     public static void main(String[] args) {
     4         // 主题
     5         WeatherData weatherData = new WeatherData();
     6         // 订阅者 向 主题 注册
     7         CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);    // Subject引用可能在取消订阅时用到。
     8         
     9         // 模拟从气象站取得数据
    10         weatherData.setMeasurements(80, 65, 30.4f);   // Current conditions: 80.0F degrees and 65.0%humidity
    11     }
    12 }
     
     
     四、变化
      JohnnyAHurricane(Weather-O-Rama气象站的CEO)刚刚来电告知,他们还需要酷热指数(HeatIndex)布告板,这是不可或缺的。
      细节如下:
      酷热指数是一个结合温度和湿度的指数,用来显示人的温度感受。可以利用温度T和相对湿度RH套用下面的公式来计算酷热指数:
       一个关于温度和相对湿度的计算公式。
     
      新注册一个观察者HeadIndexDisplay
     1 public class HeatIndexDisplay implements Observer, DisplayElement {
     2     private float tempreture;
     3     private float humidity;
     4     private Subject weatherData;
     5     
     6     public HeatIndexDisplay(Subject weatherData) {
     7         this.weatherData = weatherData;
     8         weatherData.registerObserver(this);
     9     }
    10 
    11     @Override
    12     public void display() {
    13         float T = tempreture;
    14         float RH = humidity;
    15                 // 公式不对
    16         float heatIndex =
    17                 (float) (16.923 + 1.85212 * 10-1 * T + 5.37941 * RH - 1.00254 * 10-1 * T 
    18         * RH + 9.41695 * 10-3 * T*T + 7.28898 * 10-3 * RH*RH + 3.45372 * 10-4
    19         * T*T * RH - 8.14971 * 10-4 * T * RH*RH + 1.02102 * 10-5 * T*T * RH*RH - 
    20         3.8646 * 10-5 * T*T*T + 2.91583 * 10-5 * RH*RH*RH + 1.42721 * 10-6 * T*T*T * RH 
    21         + 1.97483 * 10-7 * T * RH*RH*RH - 2.18429 * 10-8 * T*T*T * RH*RH + 8.43296 * 
    22         10-10 * T*T * RH*RH*RH - 4.81975 * 10-11 * T*T*T * RH*RH*RH);
    23         
    24         System.out.println("HeadIndex is" + heatIndex);
    25     }
    26 
    27     @Override
    28     public void update(float tempreture, float humidity, float pressure) {
    29         // TODO Auto-generated method stub
    30         this.tempreture = tempreture;
    31         this.humidity = humidity;
    32         display();
    33 
    34     }
    35 
    36 }

      

    五、讨论

       主题和观察者就 使观察者获得状态信息的正确方式:
      1、主题主动推送数据
         方便不需要再去拉数据,但是有时候不需要的也会推送过来。
      2、观察者主动取数据
         用时去取,主题需要封装、再开放setter避免暴露过多信息。
       java内置Observer这两种都支持。
       java.util包(package)内包含最基本的Observer接口与Observable类,这和Subject接口与Observer接口很相似。
      Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。
      甚至可以使用推(push)或拉(pull)的方式传送数据。
       

       setChanged()方法的作用:

        setChanged()方法可以让你在更新观察者时,有更多的弹性,你可以更适当地通知观察者。

      比方说,如果没有setChanged()方法,我们的气象站测量是如此敏锐,以致于温度计读数每十分之一度就会更新,

      这会造成WeatherData对象持续不断地通知观察者,我们并不希望看到这样的事情发生。

      如果我们希望半度以上才更新,就可以在温度差距到达半度时,调用setChanged(),进行有效的更新。

       主题
     1 public class WeatherData extends Observable{
     2     
     3     private float temperature;
     4     private float humidity;
     5     private float pressure;
     6     
     7 //    "拉"察者会利用这些方法取得WeatherData对象的状态
     8     public float getTemperature() {
     9         return temperature;
    10     }
    11 
    12     public float getHumidity() {
    13         return humidity;
    14     }
    15 
    16     public float getPressure() {
    17         return pressure;
    18     }
    19 
    20     public void setMeasurements(float temperature, float humidity, float pressure) {
    21          this.temperature = temperature;
    22          this.humidity = humidity;
    23          this.pressure = pressure;
    24          measurementsChanged();
    25     }
    26     
    27     public void measurementsChanged(){
    28         setChanged();
    29         notifyObservers();  //注意:我们没有调用notifyObservers()传送数据对象,这表示我们采用的做法是“拉”。
    30     }
    31 }

      观察者

     1 import java.util.Observable;
     2 import java.util.Observer;
     3 
     4 public class CurrentConditionsDisplay implements Observer,DisplayElement {
     5     
     6     private float temperature;
     7     private float humidity;
     8     Observable o;
     9     
    10     public CurrentConditionsDisplay(Observable o) {
    11         this.o = o;
    12         o.addObserver(this);
    13     }
    14     
    15     @Override
    16     public void update(Observable obs, Object arg) {
    17         if(obs instanceof WeatherData){
    18             WeatherData weatherData = (WeatherData) obs;
    19             this.temperature = weatherData.getTemperature();
    20             this.humidity = weatherData.getHumidity();
    21             display();
    22         }
    23     }
    24     @Override
    25     public void display() {
    26         System.out.println("Current conditions: "+ temperature +
    27             "F degrees and " + humidity +"%humidity");
    28     }
    29 
    30 }

       内置观察者模式的问题:

         1、通知观察者的次序不一定是其注册的次序。
         可以确定的是,如果我们的代码依赖之前的次序,就是错的。
        一旦观察者/可观察者的实现有所改变,通知次序就会改变,很可能产生错误的结果,也不是我们所认为的松耦合。
        2、java.util.Observable是一个类而不是一个接口
        如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟Java不支持多重继承。
        这限制了Observable的复用潜力(而增加复用潜力不正是我们使用模式最原始的动机吗?)。
        再者,因为没有Observable接口,所以你无法建立自己的实现,和Java内置的Observer API搭配使用。
        3、Observable将关键的方法保护起来
        如果你看看Observable API,你会发现setChanged()方法被保护起来了(被定义成protected)。
        这意味着:除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象中来。
        这个设计违反了第二个设计原则:“多用组合,少用继承”。
     
    六、总结
      
      观察者模式定义了对象之间一对多的关系。
     
      主题(也就是可观察者)用一个共同的接口来更新观察者。
     
      观察者和可观察者之间用松耦合方式结合(loosecoupling),可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
     
      使用此模式时,你可从被观察者处推(push)或拉(pull)数据(然而,推得方式被认为更“正确”)。
     
      有多个观察者时,不可以依赖特定的通知顺序。
     
      Java有多种观察者模式的实现,包括了通用的java.util.Observable。
     
      要注意java.util.Observable所带来的的一些问题。
     
      如果有必要的话,可以实现自己的Observable。
      
      设计原则:
        1、封装变化
        2、多用组合,少用继承
        3、针对接口编程,不针对实现编程
        4、为交互之间的松耦合设计儿努力       // 新原则
     
       『观察者模式』Obverse Pattern 在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的其他对象都会收到通知,并自动更新。
     
       一个新的模式,以松耦合方式在一系列对象之间沟通状态。我们目前还没看到观察者模式的代表人物——MVC。
       
     
     
     
     
     
     
     
  • 相关阅读:
    selenium 常见操作,使用 pywin32库 进行上传操作
    selenium 常见操作,使用 js 操作-日期框及文本框
    selenium 常见操作,js操作-将元素滚动到页面可见区域
    selenium 常见操作,使用 Keys 类来进行键盘的按键操作
    oracle性能诊断sql
    浏览器是如何处理页面元素的Download?
    websphere启动报:Could not resolve placeholder 'hibernate.hbm2ddl.auto' in string value "${hibernate.hbm2ddl.auto}"
    websphere部署不能发布war文件,提示“配置库中已存在应用程序
    websphere gc策略调整
    oracle表结构表数据导入导出
  • 原文地址:https://www.cnblogs.com/onroad2019/p/12895336.html
Copyright © 2020-2023  润新知