• 深入浅出设计模式学习笔记二:观察者模式


    观察者模式:是JDK中使用最多的模式之一,它定义了对象之间的一对多的依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。其中,将有状态的对象叫做主题Subject ,将它的所有依赖者叫做依赖者对象,又叫做观察者Observer,观察者模式的代表是MVC,以后会再单独介绍。

    如下图所示:

    使用类图的方法定义观察者模式,如下图所示:

     从类图可知,观察者模式的几个主要角色:

    抽象主题:保存所有观察者的引用,可以增加和删除观察者对象

    具体主题:当具体主题的状态发生变化的时候,通知给所有的观察者

    抽象观察者:所有观察者的接口,当主题的状态发生变化的时候,会调用其update方法

    具体观察者:实现观察者接口,以保证在主题的状态发生变化的时候,可接收更新

    两个对象之间的松耦合的设计,将对象之间的相互依赖降到了最低,能够建立有弹性的OO系统,应对变化

    观察者模式是如何实现这一设计的:

    对于主题来说,主题只知道观察值实现了Observer接口,不需要知道观察者的具体实现类是谁,做了什么以及具体的实现细节,主题对象唯一依赖的东西是一个实现了Observer接口的对象列表,所以,可以在运行时随时增加新的观察者,删除不需要的观察者

    对于观察者来说,当增加一个新的具体类的时候,不需要修改主题的代码,只需在新的观察者的类中实现观察者的接口,将其注册为观察者即可

    以实现气象站举例说明观察者模式的使用方法:

    背景说明:建立一个应用,通过使用weatherData对象追踪目前的天气状况,并分别在三种布告板上显示目前的状况,气象统计以及简单的预报,且布告板是可扩展的

      1 import java.util.ArrayList;
      2 
      3 /**
      4  * 气象属性VO
      5  */
      6 class WeatherModel{
      7     private float temperature;
      8     private float humidity;
      9     private float pressure;
     10     public WeatherModel(){}     
     11     public WeatherModel(float temperature, float humidity, float pressure) {
     12         this.temperature = temperature;
     13         this.humidity = humidity;
     14         this.pressure = pressure;
     15     }
     16     public float getTemperature() {
     17         return temperature;
     18     }
     19     public void setTemperature(float temperature) {
     20         this.temperature = temperature;
     21     }
     22     public float getHumidity() {
     23         return humidity;
     24     }
     25     public void setHumidity(float humidity) {
     26         this.humidity = humidity;
     27     }
     28     public float getPressure() {
     29         return pressure;
     30     }
     31     public void setPressure(float pressure) {
     32         this.pressure = pressure;
     33     } 
     34 } 
     35 /**
     36  * 主题接口
     37  */
     38 interface Subject{
     39     public  void  registerObserver(Observer o);  //注册观察者
     40     public  void  removeObserver(Observer o); //删除观察者
     41     public  void  notifyObserver();     //主题状态发生改变时,调用该方法,通知所有的观察者
     42 }
     43 /**
     44  * 观察者接口
     45  *
     46  */
     47 interface Observer{
     48     //更新天气状况
     49     public void update(WeatherModel weatherModel1);
     50 }
     51 
     52 interface DisplayElement{
     53     //布告板显示
     54     public void display();
     55 }
     56 /**
     57  * 天气数据类,实现主题对象接口
     58  *
     59  */
     60 class WeatherData implements Subject{
     61     //存放所有观察者list
     62     private ArrayList<Observer> observers = new ArrayList<Observer>();
     63     private WeatherModel weatherModel = new WeatherModel() ;
     64     @Override
     65     public void registerObserver(Observer o) {
     66         observers.add(o);        
     67     }
     68     @Override
     69     public void removeObserver(Observer o) {
     70         int i = observers.indexOf(o); 
     71         if (i>0) {
     72             observers.remove(i);
     73         }        
     74     }
     75     @Override
     76     public void notifyObserver() {
     77         for (int i = 0; i < observers.size(); i++) {            
     78             Observer observer = (Observer) observers.get(i);
     79             observer.update(weatherModel);        
     80         }
     81         
     82     }
     83     
     84     //天气数据改变时,保存改变的数据,再通知给所有的观察者
     85     public void setMeasurements(WeatherModel weatherModel1){
     86         
     87         weatherModel.setTemperature(weatherModel1.getTemperature());
     88         weatherModel.setHumidity(weatherModel1.getHumidity());
     89         weatherModel.setPressure(weatherModel1.getPressure());
     90         notifyObserver();
     91     }
     92     
     93 }
     94 /**
     95  * 目前天气状况的布告板 ,实现观察者,显示的接口
     96  *
     97  */
     98 class CurrentConditionsDisplay implements Observer,DisplayElement{
     99     private WeatherModel weatherModel = new WeatherModel() ;
    100     private Subject weatherData;
    101     
    102     //构造方法中需要weatherdata对象,以在主题对象中注册观察者
    103     public CurrentConditionsDisplay(Subject weatherData1) {
    104         this.weatherData = weatherData1; 
    105         weatherData.registerObserver(this);
    106         
    107     }
    108     @Override
    109     public void display() {
    110         System.out.println("Current Condition:" + weatherModel.getTemperature() + "F degrees and " + weatherModel.getHumidity() + "% humidity");
    111         
    112     }
    113 
    114     @Override
    115     public void update(WeatherModel weatherModel1) {
    116         weatherModel.setTemperature(weatherModel1.getTemperature());
    117         weatherModel.setHumidity(weatherModel1.getHumidity());
    118         display();    
    119     }
    120     
    121 }
    122 /**
    123  * 测试类
    124  *
    125  */
    126 public class ObserverTest {
    127     
    128     public static void main(String[] args) {
    129         
    130         WeatherData weatherData = new WeatherData();
    131         
    132         WeatherModel weatherModel = new WeatherModel(80, 65, 30.4f);  
    133         WeatherModel weatherModel2 = new WeatherModel(82, 70, 29.2f);
    134         
    135         CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);
    136         
    137         weatherData.setMeasurements(weatherModel);
    138         weatherData.setMeasurements(weatherModel2);
    139         
    140     }
    141 
    142 }

    运行结果:

    观察者模式是如何遵循以下设计原则的:

    1、找出程序中变化的方面,然后将其和固定不变的方面相分离:

      在观察者模式中,主题的状态时会变化的,观察者的数目和类型也是会发生变化的,由于两者是分开来的,主题状态的变化不会影响观察者,观察者的变化也不会影响主题;

    2、针对接口编程,不针对实现编程:

      主题和观察者都使用接口,观察者利用主题的接口进行注册,主题利用观察者的接口进行通知;

    3、多用组合,少用继承:

      利用组合将观察者组合进主题对象中,对象之间的关系不是继承产生的,而是在运行时利用组合产生的;

     Java API有内置的观察者模式,与上述实现的有一点差异,以气象站为例说明使用如何使用Java API内置的观察者模式完成开发:

    1、具体观察者对象实现观察者接口,调用addObserver()方法,将对象变成观察者,不想当观察者时,调用deleteObserver()方法;

    2、具体主题对象继承java.util.Observable类,这里,主题对象又叫做可观察者,然后,调用setChanged()方法,标记状态已经改变的事实,最后调用两种notifyObservers()方法中的一个:notifyObservers() 或者 notifyObservers(Object arg),当可观察者想主动把数据推给观察者时,可以把数据传送给notifyObservers(Object arg)方法,否则,观察者必须从可观察者中拉数据

    3、观察者接收通知,实现update方法,update(Observable obs,Object args),其中第一个变量为主题本身,方便让观察者知道是哪一个主题通知它,第二个变量是数据对象,没有则为空

      1 package designPattern;
      2 import java.util.Observable;
      3 import java.util.Observer;
      4 
      5 /**
      6  * 气象属性VO
      7  */
      8 class WeatherModel{
      9     private float temperature;
     10     private float humidity;
     11     private float pressure;
     12     public WeatherModel(){}     
     13     public WeatherModel(float temperature, float humidity, float pressure) {
     14         this.temperature = temperature;
     15         this.humidity = humidity;
     16         this.pressure = pressure;
     17     }
     18     public float getTemperature() {
     19         return temperature;
     20     }
     21     public void setTemperature(float temperature) {
     22         this.temperature = temperature;
     23     }
     24     public float getHumidity() {
     25         return humidity;
     26     }
     27     public void setHumidity(float humidity) {
     28         this.humidity = humidity;
     29     }
     30     public float getPressure() {
     31         return pressure;
     32     }
     33     public void setPressure(float pressure) {
     34         this.pressure = pressure;
     35     } 
     36 } 
     37 
     38 interface DisplayElement{
     39     //布告板显示
     40     public void display();
     41 }
     42 /**
     43  * 天气数据类,继承Observable类
     44  *
     45  */
     46 class WeatherData extends Observable{
     47     private WeatherModel weatherModel = new WeatherModel() ;
     48     
     49     public void measurementsChanged(){
     50         //标记状态改变的事实
     51         setChanged();
     52         //使用的是无参数的方法,说明是观察者从可观察者中拉数据,不是可观察者主动发生数据给观察者
     53         notifyObservers();
     54     }
     55     //天气数据改变时,保存改变的数据,再通知给所有的观察者
     56     public void setMeasurements(WeatherModel weatherModel1){
     57         
     58         weatherModel.setTemperature(weatherModel1.getTemperature());
     59         weatherModel.setHumidity(weatherModel1.getHumidity());
     60         weatherModel.setPressure(weatherModel1.getPressure());
     61         measurementsChanged();
     62     }
     63     
     64     //观察者会利用get方法取得可观察者的状态
     65     public WeatherModel getWeatherModel() {
     66         return weatherModel;
     67     }
     68     public void setWeatherModel(WeatherModel weatherModel) {
     69         this.weatherModel = weatherModel;
     70     }
     71     
     72 }
     73 /**
     74  * 目前天气状况的布告板 ,实现观察者,显示的接口
     75  *
     76  */
     77 class CurrentConditionsDisplay implements Observer,DisplayElement{
     78     private WeatherModel weatherModel = new WeatherModel() ;
     79     Observable observable;
     80     
     81     public CurrentConditionsDisplay(){}
     82     
     83     @Override
     84     public void display() {
     85         System.out.println("Current Condition:" + weatherModel.getTemperature() + "F degrees and " + weatherModel.getHumidity() + "% humidity");
     86         
     87     }
     88 
     89     public void update(Observable obs,Object args) {
     90         if (obs instanceof WeatherData) {
     91             
     92             WeatherData weatherData = (WeatherData) obs ;
     93             weatherModel.setTemperature(weatherData.getWeatherModel().getTemperature());
     94             weatherModel.setHumidity(weatherData.getWeatherModel().getHumidity());
     95             display();    
     96             
     97         }
     98         
     99     }
    100     
    101 }
    102 /**
    103  * 测试类
    104  *
    105  */
    106 public class ObserverByJava {
    107     
    108     public static void main(String[] args) {
    109         
    110         WeatherData weatherData = new WeatherData();
    111         
    112         WeatherModel weatherModel = new WeatherModel(80, 65, 30.4f);  
    113         WeatherModel weatherModel2 = new WeatherModel(82, 70, 29.2f);
    114         
    115         CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay();
    116         //注册观察者
    117         weatherData.addObserver(currentConditionsDisplay);
    118         
    119         weatherData.setMeasurements(weatherModel);
    120         weatherData.setMeasurements(weatherModel2);
    121         
    122     }
    123 
    124 }

    使用Java内置的观察者模式优缺点:

    优点:方便使用

    缺点:违反了OO设计原则中的“针对接口编程”和“少用继承,多用组合”,因为java.util.Observable是一个类,所以必须设计一个类继承它,如果该类想要同时拥有两个类的行为,就会陷入两难,因为java不支持多种继承,限制了Observable复用的潜力,违反了第一个设计原则;并且通过查看源码可知,setChanged()被定义为protected,意味着只能继承Observable,不能通过组合的方式使用Observable,违反了第二个设计原则。

  • 相关阅读:
    索引
    mysql事务
    centos 7 gitlab安装服务器
    内网穿透工具 frp使用
    eslint配置
    nodejs连接mongodb(密码)
    插入排序
    直接插入排序
    koa中 log4js使用
    JS中的prototype、__proto__与constructor(图解)
  • 原文地址:https://www.cnblogs.com/miaowu1314/p/5465397.html
Copyright © 2020-2023  润新知