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

观察者模式:是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,违反了第二个设计原则。

posted @ 2016-05-13 08:58  喵呜1314  阅读(251)  评论(0编辑  收藏  举报