今天把第二章的观察者模式看完了,收获挺大,感觉这本书买的还是挺直的。
本章又有了一个新的设计原则:为了交互对象之间的松耦合设计而努力。
观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
现整理书中关于观察者模式的代码如下:
一、自己设计观察者模式实现根据气象站的数据实时更新布告栏
1、被观察者接口Subject.java
package com.sq.observerPattern.weather;
/**
* 主题接口
* @author SQ
*
*/
public interface Subject {
/**
* 注册观察者
* @param o
*/
public void registerObserver(Observer o);
/**
* 移除一个注册的观察者
* @param o
*/
public void removeObserver(Observer o);
/**
* 通知所有注册了的观察者主题的数据更新了
*/
public void notifyObservers();
}
2、实际被观察者WeatherData.java
package com.sq.observerPattern.weather;
import java.util.ArrayList;
/**
* 实际的被观察者
* @author SQ
*
*/
public class WeatherData implements Subject{
private ArrayList<Observer> observers; //记录注册的观察者
private float temperature; //温度
private float humidity; //湿度
private float pressure; //气压
public WeatherData() {
observers = new ArrayList<Observer>();
}
@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 notifyObservers() {
for(int i=0; i<observers.size(); i++){
Observer observer = observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
/**
* 当从气象站得到更新观测值时通知观察者
*/
public void measurementsChanged() {
notifyObservers();
}
/**
* 获取更新的观测值
* @param temperature 温度
* @param humidity 湿度
* @param pressure 气压
*/
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}
3、自定义的观察者接口Observer.java
package com.sq.observerPattern.weather;
/**
* 观察者接口
* @author SQ
*
*/
public interface Observer {
public void update(float temperature, float humidity, float pressure);
}
4、自定义的用于显示布告栏内容的接口DisplayElement.java
package com.sq.observerPattern.weather;
/**
* 显示布告栏内容的接口
* @author SQ
*
*/
public interface DisplayElement {
/**
* 在布告栏中显示相应的内容
*/
public void display();
}
5、目前天气状态布告栏CurrentConditionDisplay.java
package com.sq.observerPattern.weather;
/**
* 目前天气状态布告栏
* @author SQ
*
*/
public class CurrentConditionDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
public void display() {
System.out.println("Current conditions: " + temperature + "F degrees and " +
humidity + "% humidity.");
}
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
}
6、天气预报布告栏ForecastDisplay.java
package com.sq.observerPattern.weather;
/**
* 天气预报布告栏
* @author SQ
*
*/
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);
}
public void update(float temp, float humidity, float pressure) {
lastPressure = currentPressure;
currentPressure = pressure;
display();
}
public void display() {
System.out.print("Forecast: ");
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");
}
}
}
7、气象统计布告栏StatisticsDisplay.java
package com.sq.observerPattern.weather;
/**
* 气象统计布告栏
* @author SQ
*
*/
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);
}
public void update(float temp, float humidity, float pressure) {
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
+ "/" + maxTemp + "/" + minTemp);
}
}
8、酷热指数布告栏HeatIndexDisplay.java酷热指数布告栏 HeatIndexDisplay.java
package com.sq.observerPattern.weather;
/**
* 酷热指数布告栏
* @author SQ
*
*/
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);
}
}
9、测试程序WeatherStation.java,用来模拟气象站检测到的数据变化
package com.sq.observerPattern.weather;
/**
* 测试程序,即气象站
* @author SQ
*
*/
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay cunrrentDisplay = 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);
}
}
10、输出结果
Current conditions: 80.0F degrees and 65.0% humidity.
Avg/Max/Min temperature = 80.0/80.0/80.0
Forecast: Improving weather on the way!
Heat index is 82.95535
Current conditions: 82.0F degrees and 70.0% humidity.
Avg/Max/Min temperature = 81.0/82.0/80.0
Forecast: Watch out for cooler, rainy weather
Heat index is 86.90124
Current conditions: 78.0F degrees and 90.0% humidity.
Avg/Max/Min temperature = 80.0/82.0/78.0
Forecast: More of the same
Heat index is 83.64967
二、使用java内置的观察者模式实现气象站,java提供了被观察者的基类Observable和观察者的接口Observer。
1、WeatherData.java
package com.sq.observerPattern.weatherObservable;
import java.util.ArrayList;
import java.util.Observable;
public class WeatherData extends Observable{
private float temperature; //温度
private float humidity; //湿度
private float pressure; //气压
public WeatherData() { }
/**
* 当从气象站得到更新观测值时通知观察者
*/
public void measurementsChanged() {
setChanged();
notifyObservers();
}
/**
* 获取更新的观测值
* @param temperature 温度
* @param humidity 湿度
* @param pressure 气压
*/
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、 目前天气状态布告栏 CurrentConditionDisplay.java
package com.sq.observerPattern.weatherObservable;
import java.util.Observable;
import java.util.Observer;
/**
* 目前天气状态布告栏
* @author SQ
*
*/
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.");
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData) o;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
}
3、 天气预报布告栏 ForecastDisplay.java
package com.sq.observerPattern.weatherObservable;
import java.util.Observable;
import java.util.Observer;
/**
* 天气预报布告栏
* @author SQ
*
*/
public class ForecastDisplay implements Observer, DisplayElement{
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.print("Forecast: ");
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) o;
lastPressure = currentPressure;
currentPressure = weatherData.getPressure();
display();
}
}
}
4、 气象统计布告栏 StatisticsDisplay.java
package com.sq.observerPattern.weatherObservable;
import java.util.Observable;
import java.util.Observer;
/**
* 气象统计布告栏
* @author SQ
*
*/
public class StatisticsDisplay implements Observer, DisplayElement{
Observable observable;
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum= 0.0f;
private int numReadings;
public StatisticsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData) o;
float temp = weatherData.getTemperature();
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
}
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
+ "/" + maxTemp + "/" + minTemp);
}
}
5、 酷热指数布告栏 HeatIndexDisplay.java
package com.sq.observerPattern.weatherObservable;
import java.util.Observable;
import java.util.Observer;
/**
* 酷热指数布告栏
* @author SQ
*
*/
public class HeatIndexDisplay implements Observer, DisplayElement{
Observable observable;
float heatIndex = 0.0f;
public HeatIndexDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData) o;
heatIndex = computeHeatIndex(weatherData.getTemperature(), weatherData.getHumidity());
}
}
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、 测试程序 WeatherStation.java,用来模拟气象站检测到的数据变化
package com.sq.observerPattern.weatherObservable;
/**
* 测试程序,即气象站
* weatherObservable包中是使用了java内置的观察者模式实现的气象站
* WeatherData继承了系统的可观察者Observable类,而观察者们继承了系统的观察者Observer接口
* @author SQ
*
*/
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay cunrrentDisplay = 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、输出结果
Forecast: Improving weather on the way!
Avg/Max/Min temperature = 80.0/80.0/80.0
Current conditions: 80.0F degrees and 65.0% humidity.
Forecast: 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.
Forecast: More of the same
Avg/Max/Min temperature = 80.0/82.0/78.0
Current conditions: 78.0F degrees and 90.0% humidity.
三、总结
使用Java内置的观察者模式的输出结果和我们之前的文字的输出次序不一样,这就使得我们在一些场合下不能依赖于观察者被通知的次序。
不知道你看出来没有Observable是一个类,而不是一个接口,而且它甚至没有实现一个借口,这样我们使用时就必须继承它,但如果我还有一个类要继承,那怎么办,Java并不支持多重继承。它违反了“针对接口编程,而非针对实现编程”的设计原则。而且Observable把它关键的方法setChanged()保护了起来(设为了protected),这样我们也不能使用组合的方式使用它,它违反了“多用组合,少用继承”的设计原则。