设计模式学习--观察者模式(Oberser Pattern)
来源:互联网 发布:mwc飞控源码 编辑:程序博客网 时间:2024/06/06 19:59
设计模式学习–观察者模式(Oberser Pattern)
什么是观察者模式?
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
怎么设计一个观察者模式的程序?
确定两个对象:1. 主题
2.观察者
确定这两个对象之间的关系:
主题对象管理某些数据,一旦数据发生改变,会主动向观察者进行通知,然而观察者不必向主题进行索取。
主题并不知道具体的观察者是谁,这是它们之间的关系。
以上涉及到的设计原则:
为了交互对象之间的松耦合设计而努力
具体实例:气象站的实现
- 定义一个主题接口Subject
[java] view plaincopypackage observerPattern; /** * 主题接口 */ public interface Subject { public void registerObserver(Observer o); //这两个方法都需要一个观察者作为变量,该观察者是用那个来注册和删除的 public void removeObserver(Observer o); public void notifyObserver(); //当主题状态发生改变时,这个方法会被调用,以通知所有的观察者 }
2. 定义一个观察者接口Observer
[java] view plaincopypackage observerPattern; /** * 观察者接口 */ public interface Observer { public void update(float temp, float humidity, float pressure); }
3. 定义一般气象布告板接口DisplayElement
[java] view plaincopypackage observerPattern; /** * 公告板接口 */ public interface DisplayElement { public void display(); }
4. 定义主题类:WeatherData实现接口
[java] view plaincopypackage observerPattern; import java.util.ArrayList; /** * WeatherData实现了Subject接口 */ public class WeatherData implements Subject { private ArrayList observers; //用于记录观察者 private float temperature; //温度 private float humidity; //湿度 private float pressure; //压力 public WeatherData() { observers = new ArrayList(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if(i >= 0) { observers.remove(i); } } @Override public 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. 定义四个布告板类实现观察者接口和布告板接口
[java] view plaincopypackage observerPattern; /** * 观察者类实现观察者接口和显示板接口 */ 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); //注册 } @Override public void display() { System.out.println("Current coditions: " + temperature + "F degress and " + humidity + "% humidity"); } @Override public void update(float temp, float humidity, float pressure) { this.temperature = temp; this.humidity = humidity; display(); } } [java] view plaincopypackage observerPattern; /** * 天气统计布告板 */ 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); } @Override public void display() { System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp); } @Override public void update(float temp, float humidity, float pressure) { tempSum += temp; numReadings++; if(temp > maxTemp) { maxTemp = temp; } if(temp < minTemp) { minTemp = temp; } display(); } } [java] view plaincopypackage observerPattern; /** * 天气预报布告板 */ 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); } @Override public 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"); } } @Override public void update(float temp, float humidity, float pressure) { lastPressure = currentPressure; currentPressure = pressure; display(); } } [java] view plaincopypackage observerPattern; /** * 酷热指数布告板 * 注:那个计算酷热指数的公式不必深究 */ 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. 来吧,开始测试
[java] view plaincopypackage observerPattern; /** * 测试类 * */ 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. 测试结果:
[plain] view plaincopy
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(不再需要自定义接口了,但这样真的好吗?)
[java] view plaincopy
package weatherObservable;
import java.util.Observable;
/**
* 使用Java内置的观察者模式
*/
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个布告板中的代码稍微发生了点变化
[java] view plaincopypackage weatherObservable; import java.util.Observable; import java.util.Observer; /** * 实现Java内置的观察者接口,布告板不变 */ public class CurrentConditionDisplay implements Observer, DisplayElement{ Observable observable; private float temperature; private float humidity; public CurrentConditionDisplay(Observable observable) { this.observable = observable; observable.addObserver(this); //登记为观察者 } @Override public void display() { System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity"); } /** * 在这个方法当中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和温度测量值,最后调用display(); */ @Override public void update(Observable obs, Object arg) { if(obs instanceof WeatherData) { WeatherData weatherData = (WeatherData) obs; this.temperature = weatherData.getTemperature(); this.humidity = weatherData.getHumidity(); display(); } } } [java] view plaincopypackage weatherObservable; import java.util.Observable; import java.util.Observer; /** * 天气预报布告板 */ 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); } @Override public 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"); } } @Override public void update(Observable o, Object arg) { if (o instanceof WeatherData) { WeatherData weatherData = (WeatherData)observable; lastPressure = currentPressure; currentPressure = weatherData.getPressure(); display(); } } } [java] view plaincopypackage weatherObservable; import java.util.Observable; import java.util.Observer; /** * 天气统计布告板 */ 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); } @Override public void display() { System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings) + "/" + maxTemp + "/" + minTemp); } @Override public 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(); } } } [java] view plaincopypackage weatherObservable; import java.util.Observable; import java.util.Observer; /** * 酷热指数布告板 * 注:那个计算酷热指数的公式不必深究 */ 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); } @Override public 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. 测试类不变
[java] view plaincopypackage weatherObservable; /** * 测试类 */ 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. 但测试结果发生了变化:观察者被通知的次序发生了变化
[plain] view plaincopy
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
- 设计模式学习--观察者模式(Oberser Pattern)
- 设计模式学习--观察者模式(Observer Pattern)
- 设计模式学习(二)-- 观察者模式 Observer Pattern
- 设计模式学习笔记--观察者模式(Observer Pattern)
- 设计模式学习—观察者模式(Observer Design Pattern)
- 设计模式学习总结:观察者模式(Observer Pattern)
- 观察者设计模式(Observer Pattern)
- 设计模式17:Observer Pattern (观察者模式)
- 设计模式-观察者模式(Observer Pattern)
- 设计模式之观察者模式(Observer Pattern)
- c++设计模式:观察者模式(Observer Pattern)
- 设计模式 - 观察者模式(Observer Pattern) 详解
- 设计模式 - 观察者模式(Observer Pattern) 详解
- 设计模式 - 观察者模式(Observer Pattern) 详解
- 设计模式心得:观察者模式 (observer pattern)
- 设计模式-观察者模式(observer pattern)
- 设计模式之观察者模式---Observer Pattern
- 设计模式--观察者模式【Observer Pattern】
- MySQL学习笔记_11_Linux下C++/C连接MySQL数据库(一)
- MyBatis Invalid bound statement
- 埃拉托斯特尼筛法(Sieve of Eratosthenes)简单c实现
- 黑马程序员——Java高新技术——JDK1.5版本的新特性泛型
- Linux下硬盘读写测试以及编译用时分析
- 设计模式学习--观察者模式(Oberser Pattern)
- hibernate连接mysql配置文件
- 存档IMAP或POP3汇集邮箱
- PMP项目管理之核心:项目经理角色定位
- 图像预处理——二值化(大律法)
- js原型
- 关于 oracle 归档开启以及使用的相关知识
- ubuntu10.04如何升级到12.04
- Activity启动模式和Intent Flags以及栈关联粗解