设计模式学习--观察者模式(Oberser Pattern)

2013年5月18日 天气:热! 
下午15:28  设计模式学习中
学习者:小巫

什么是观察者模式?

定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

怎么设计一个观察者模式的程序?

确定两个对象:1. 主题
  2.观察者

确定这两个对象之间的关系:

主题对象管理某些数据,一旦数据发生改变,会主动向观察者进行通知,然而观察者不必向主题进行索取。
主题并不知道具体的观察者是谁,这是它们之间的关系。

以上涉及到的设计原则:

为了交互对象之间的松耦合设计而努力
具体实例:气象站的实现
1. 定义一个主题接口Subject
package observerPattern;
/*** 主题接口* @author wwj**/
public interface Subject {public void registerObserver(Observer o); //这两个方法都需要一个观察者作为变量,该观察者是用那个来注册和删除的public void removeObserver(Observer o);public void notifyObserver();              //当主题状态发生改变时,这个方法会被调用,以通知所有的观察者
}

2. 定义一个观察者接口Observer

package observerPattern;
/*** 观察者接口* @author wwj**/
public interface Observer {public void update(float temp, float humidity, float pressure);
}
3. 定义一般气象布告板接口DisplayElement
package observerPattern;/*** 公告板接口* @author wwj**/
public interface DisplayElement {public void display();
}

4. 定义主题类:WeatherData实现接口

package observerPattern;import java.util.ArrayList;/*** WeatherData实现了Subject接口* @author wwj**/
public class WeatherData implements Subject {private ArrayList observers;   //用于记录观察者private float temperature;     //温度private float humidity;         //湿度private float pressure;         //压力public WeatherData() {observers = new ArrayList();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if(i >= 0) {observers.remove(i);}}@Overridepublic void notifyObserver() {for(int i = 0; i < observers.size(); i++) {Observer observer = (Observer)observers.get(i);observer.update(temperature, humidity, pressure);}}public void measurementsChanged() {notifyObserver();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}}

5. 定义四个布告板类实现观察者接口和布告板接口

package observerPattern;
/*** 观察者类实现观察者接口和显示板接口* @author wwj**/
public class CurrentConditionDisplay implements Observer, DisplayElement {private float temperature;private float humidity;private Subject weathderData;public CurrentConditionDisplay(Subject weathderData) {this.weathderData = weathderData;weathderData.registerObserver(this);        //注册}@Overridepublic void display() {System.out.println("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");}@Overridepublic void update(float temp, float humidity, float pressure) {this.temperature = temp;this.humidity = humidity;display();}}
package observerPattern;
/*** 天气统计布告板* @author wwj**/
public class StatisticsDisplay implements Observer, DisplayElement {private float maxTemp = 0.0f;; //最大温度private float minTemp = 200; //最小温度private float tempSum = 0.0f;    //统计温度和private int numReadings;     //统计温度次数private WeatherData weatherData;public StatisticsDisplay(WeatherData weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void display() {System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);}@Overridepublic void update(float temp, float humidity, float pressure) {tempSum += temp;numReadings++;if(temp > maxTemp) {maxTemp = temp;}if(temp < minTemp) {minTemp = temp;}display();}}
package observerPattern;
/*** 天气预报布告板* @author wwj**/
public class ForecastDisplay implements Observer, DisplayElement {private float currentPressure = 29.92f;  //当前气压private float lastPressure;               //以往气压private WeatherData weatherData;public ForecastDisplay(WeatherData weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void display() {System.out.println("Forcast:");if(currentPressure > lastPressure) {System.out.println("Improving weather on the way!");} else if(currentPressure == lastPressure) {System.out.println("more of the same");} else if(currentPressure < lastPressure) {System.out.println("Watch out for cooler, rainy weather");}}@Overridepublic void update(float temp, float humidity, float pressure) {lastPressure = currentPressure;currentPressure = pressure;display();}}
package observerPattern;/*** 酷热指数布告板* * @author wwj* 注:那个计算酷热指数的公式不必深究*/
public class HeatIndexDisplay implements Observer, DisplayElement {float heatIndex = 0.0f;private WeatherData weatherData;public HeatIndexDisplay(WeatherData weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}public void update(float t, float rh, float pressure) {heatIndex = computeHeatIndex(t, rh);display();}private float computeHeatIndex(float t, float rh) {float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +(0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +0.000000000843296 * (t * t * rh * rh * rh)) -(0.0000000000481975 * (t * t * t * rh * rh * rh)));return index;}public void display() {System.out.println("Heat index is " + heatIndex);}
}

6. 来吧,开始测试

package observerPattern;/*** 测试类* @author wwj**/
public class WeatherStation {public static void main(String[] args) {//建立一个WeatherData对象WeatherData weatherData = new WeatherData();//第一个布告板CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);// 模拟新的气象数据weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}

7. 测试结果:

Current coditions: 80.0F degress and 65.0% humidity
Avg/Max/Min temperature = 80.0/80.0/80.0
Forcast:
Improving weather on the way!
Heat index is 82.95535
Current coditions: 82.0F degress and 70.0% humidity
Avg/Max/Min temperature = 81.0/82.0/80.0
Forcast:
Watch out for cooler, rainy weather
Heat index is 86.90124
Current coditions: 78.0F degress and 90.0% humidity
Avg/Max/Min temperature = 80.0/82.0/78.0
Forcast:
more of the same
Heat index is 83.64967
以上的观察者模式实现是通过主题以“推”的方式通知观察者们,观察者可以在一次通知中一口气得到所有东西。
因为观察者与主题发生了争吵,观察者有自己的想法,希望能“拉”走主题的状态,然而Java内置的Observer模式就支持这样,下面来看看吧。
1. 继承Observable类的WeatherData(不再需要自定义接口了,但这样真的好吗?)
package weatherObservable;import java.util.Observable;
/*** 使用Java内置的观察者模式* @author wwj**/
public class WeatherData extends Observable {private float temperature;private float humidity;private float pressure;/*** 我们的构造器不再需要为了记住观察者们而建立数据结构了*/public WeatherData(){}public void measurementsChanged() {setChanged();        //Observable类方法notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}public float getTemperature() {return temperature;}public float getHumidity() {return humidity;}public float getPressure() {return pressure;}}

2. 4个布告板中的代码稍微发生了点变化

package weatherObservable;import java.util.Observable;
import java.util.Observer;/*** 实现Java内置的观察者接口,布告板不变* @author wwj**/
public class CurrentConditionDisplay implements Observer, DisplayElement{Observable observable;private float temperature;private float humidity;public CurrentConditionDisplay(Observable observable) {this.observable = observable;observable.addObserver(this);      //登记为观察者}@Overridepublic void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}/*** 在这个方法当中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和温度测量值,最后调用display();*/@Overridepublic void update(Observable obs, Object arg) {if(obs instanceof WeatherData) {WeatherData weatherData = (WeatherData) obs;this.temperature = weatherData.getTemperature();this.humidity = weatherData.getHumidity();display();}}}
package weatherObservable;import java.util.Observable;
import java.util.Observer;/*** 天气预报布告板* @author wwj**/
public class ForecastDisplay implements Observer, DisplayElement {private Observable observable;private float currentPressure = 29.92f;    //当前气压private float lastPressure;               //以往气压public ForecastDisplay(Observable observable) {this.observable = observable;observable.addObserver(this);}@Overridepublic void display() {System.out.println("Forcast:");if(currentPressure > lastPressure) {System.out.println("Improving weather on the way!");} else if(currentPressure == lastPressure) {System.out.println("more of the same");} else if(currentPressure < lastPressure) {System.out.println("Watch out for cooler, rainy weather");}}@Overridepublic void update(Observable o, Object arg) {if (o instanceof WeatherData) {WeatherData weatherData = (WeatherData)observable;lastPressure = currentPressure;currentPressure = weatherData.getPressure();display();}}}
package weatherObservable;import java.util.Observable;
import java.util.Observer;/*** 天气统计布告板* @author wwj**/
public class StatisticsDisplay implements Observer, DisplayElement {private float maxTemp = 0.0f;; //最大温度private float minTemp = 200; //最小温度private float tempSum = 0.0f;    //统计温度和private int numReadings;     //统计温度次数private Observable observable;public StatisticsDisplay(Observable observable) {this.observable = observable;observable.addObserver(this);}@Overridepublic void display() {System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp);}@Overridepublic void update(Observable obs, Object arg) {if(obs instanceof WeatherData) {WeatherData weatherData = (WeatherData)obs;float temp = weatherData.getTemperature();tempSum += temp;numReadings++;if (temp > maxTemp) {maxTemp = temp;}if (temp < minTemp) {minTemp = temp;}display();}}}
package weatherObservable;import java.util.Observable;
import java.util.Observer;/*** 酷热指数布告板* * @author wwj* 注:那个计算酷热指数的公式不必深究*/
public class HeatIndexDisplay implements Observer, DisplayElement {float heatIndex = 0.0f;private WeatherData weatherData;private Observable observable;public HeatIndexDisplay(Observable observable) {this.observable = observable;observable.addObserver(this);}private float computeHeatIndex(float t, float rh) {float index = (float)((16.923 + (0.185212 * t) + (5.37941 * rh) - (0.100254 * t * rh) + (0.00941695 * (t * t)) + (0.00728898 * (rh * rh)) + (0.000345372 * (t * t * rh)) - (0.000814971 * (t * rh * rh)) +(0.0000102102 * (t * t * rh * rh)) - (0.000038646 * (t * t * t)) + (0.0000291583 * (rh * rh * rh)) + (0.00000142721 * (t * t * t * rh)) + (0.000000197483 * (t * rh * rh * rh)) - (0.0000000218429 * (t * t * t * rh * rh)) +0.000000000843296 * (t * t * rh * rh * rh)) -(0.0000000000481975 * (t * t * t * rh * rh * rh)));return index;}public void display() {System.out.println("Heat index is " + heatIndex);}@Overridepublic void update(Observable obs, Object arg) {if(obs instanceof WeatherData) {WeatherData weatherData = (WeatherData)observable;float t = weatherData.getTemperature();float rh = weatherData.getHumidity();heatIndex = computeHeatIndex(t, rh);}display();}
}

3. 测试类不变

package weatherObservable;/*** 测试类* @author wwj**/
public class WeatherStation {public static void main(String[] args) {//建立一个WeatherData对象WeatherData weatherData = new WeatherData();//第一个布告板CurrentConditionDisplay currentDisplay = new CurrentConditionDisplay(weatherData);StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);HeatIndexDisplay heatIndexDisplay = new HeatIndexDisplay(weatherData);// 模拟新的气象数据weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}

4. 但测试结果发生了变化:观察者被通知的次序发生了变化

Heat index is 82.95535
Forcast:
Improving weather on the way!
Avg/Max/Min temperature = 80.0/80.0/80.0
Current conditions: 80.0F degrees and 65.0% humidity
Heat index is 86.90124
Forcast:
Watch out for cooler, rainy weather
Avg/Max/Min temperature = 81.0/82.0/80.0
Current conditions: 82.0F degrees and 70.0% humidity
Heat index is 83.64967
Forcast:
more of the same
Avg/Max/Min temperature = 80.0/82.0/78.0
Current conditions: 78.0F degrees and 90.0% humidity

以上的实现被认为不是那么“正确”的,为什么呢?

有以下原因:
1. Observable是一个“类”,而不是一个接口,也没有实现一个接口,限制了它的使用和复用。
2. Observable将关键的方法保护起来了,违反了 “多用组合,少用继承”的设计原则。
当然在JDK中不只有这个地方用到了观察者模式,比如以下几个地方也用到了:
1. Swing
2. JavaBeans
3. RMI
好啦,第二个设计模式:观察者模式被小巫收入囊中,这实在太有趣了。
下一个模式将会是:装饰者模式。

设计模式学习--观察者模式(Observer Pattern)相关推荐

  1. 锈才学设计模式之 —— 观察者模式(Observer Pattern)

    锈才学设计模式之  -- 观察者模式 观察者模式:定义对象的一对多的关系,这样当主题对象改变状态时,其它的观察者对象都会收到通知,自动更新. 说明: 在真实世界中存在很多类似的模型,比如:订报纸,找中 ...

  2. 设计模式-观察者模式(Observer Pattern)

    设计模式-观察者模式 观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应.在观察者模式中,发生改变的对象称为观 ...

  3. 设计模式 - 观察者模式(Observer Pattern) Java内置 用法

    观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...

  4. 设计模式:观察者模式--Observer

    一.什么是观察者模式 1.生活中的观察者模式 1.警察抓小偷 在现实生活中,警察抓小偷是一个典型的观察者模式「这以一个惯犯在街道逛街然后被抓为例子」,这里小偷就是被观察者,各个干警就是观察者,干警时时 ...

  5. 设计模式 -行为型模式_ 观察者模式Observer Pattern 之 JDK内置的实现

    文章目录 概念 Code [主题] [观察者s] 观察者一 观察者二 [测试] 源码分析 java.util.Observable 目标类 java.util.Observer 观察者接口 notif ...

  6. 极速理解设计模式系列:2.观察者模式(Observer Pattern)

    4个角色:被观察者(Subject/目标对象接口),具体被观察者(ConcreteSubject/具体目标对象),观察者(Observer),具体观察者(ConcreteObserver)       ...

  7. 设计模式初探-观察者模式(OBSERVER)又称发布-订阅(Publish-Subscribe)依赖(Dependents)

    观察者模式(OBSERVER),又称发布-订阅(Publish-Subscribe),依赖(Dependents),通过定义对象间的一对多的依赖关系,达到当一个对象的状态发生改变时,所有依赖于它的对象 ...

  8. Android开发中常见的设计模式深入浅出——观察者模式Observer

    ##最近老大写的Android项目里用到了RxBus然后我就去百度了 让我先了解RxJava 然后RxJava又是由观察者模式的变种写的 所以打算从头学一遍!!! 观察者模式 Observer 顾名思 ...

  9. 设计模式(行为型)之观察者模式(Observer Pattern)

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

最新文章

  1. 一步一步学习hadoop(七)
  2. Android Studio打包~安卓打包踩坑及总结
  3. LeetCode 1153. 字符串转化(哈希)
  4. 【AI视野·今日NLP 自然语言处理论文速览 第二十八期】Wed, 1 Dec 2021
  5. 一起学Windows Phone7开发(十三.三 输入控件)
  6. ajax编写前台的时候,列表显示部分,表格显示不出来,但是可以获取到数据
  7. 什么是技术档案(Technical Archives)?
  8. python硬件测试开发工程师_硬件测试工程师
  9. 【CV】用于计算机视觉的 Transformer 高被引综述
  10. java 成绩管理系统 报告_Java学生成绩管理系统实验报告
  11. python操作微信电脑版_【Python】【微信】+操作电脑版微信
  12. VMware安装统信UOS
  13. python 复数的模_如何使用abs在python中返回复数?
  14. sql计算字段中字数的个数
  15. android11 前摄相头隐藏闪光灯图标
  16. 量化交易入门笔记-策略常用对象
  17. php im即时消息,im即时通讯php
  18. 《食物语》运营复盘 二次元女性向手游的发行之道
  19. FreeDOM —— 一个可迁移的网页信息抽取模型
  20. What?Tomcat 竟然也算中间件?

热门文章

  1. [C程序设计]输出所有的“水仙花数”。
  2. 没有权限访问网络资源/Windows7虚拟机共享文件
  3. 最好的防御就是进攻 任正非
  4. imx6q 添加intel PCIE网卡
  5. Nvme pcie有线千兆网卡
  6. pci串行端口找不到驱动程序_PCI网卡和PCIE网卡
  7. .NET ORM 仓储层必备的功能介绍之 FreeSql Repository 实现篇
  8. Attempt to invoke virtual method ‘void cn.jiguang.share.android.api.AbsPlatform.notifyError
  9. PXE+Kickstart无人值守安装系统
  10. 新颖的自我介绍_精选简单新颖的自我介绍