HeadFirst设计模式之观察者模式

今天把第二章的观察者模式看完了,收获挺大,感觉这本书买的还是挺直的。

本章又有了一个新的设计原则:为了交互对象之间的松耦合设计而努力。

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

现整理书中关于观察者模式的代码如下:

一、自己设计观察者模式实现根据气象站的数据实时更新布告栏

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),这样我们也不能使用组合的方式使用它,它违反了“多用组合,少用继承”的设计原则。



你可能感兴趣的:(HeadFirst设计模式之观察者模式)