此系统的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。
具体来说该应用需要:利用WeatherDate对象从气象站取得数据,并更新三个布告板:目前状况、气象统计和天气预报。
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。
主题和观察者定义了一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格,观察者可能因此新值而更新。
关于观察者的一切,主题只知道观察者实现了某个接口(Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节,将对象之间的相互依赖性降到最低。符合了
设计原则:
为了交互对象之间的松耦合设计和努力。
结合气象站的需求和观察者模式的定义,得到气象站的设计图如下:
从图中可以看出,有三个接口需要建立:Subject、Observer、DisplayElement
Subject:
package com.lissdy;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
Observer:
package com.lissdy;
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
DisplayElement:
package com.lissdy;
public interface DisplayElement {
public void display();
}
在WeatherData中实现Subject接口:
package com.lissdy;
import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float pressure;
private float humidity;
public WeatherData() {
observers = new ArrayList(); //加上一个ArrayList来记录观察者,此ArrayList是在构造器中产生的
}
public void registerObserver(Observer o) {
observers.add(o); //有观察者注册时,将其加到ArrayList后面
}
public void removeObserver(Observer o) {
int i = observers.indexOf(o); //观察者取消注册时,将其从ArrayList中删除
if (i >= 0) {
observers.remove(i);
}
}
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();
}
}
建立当前天气状况和温度统计的布告板:
当前天气状况:
package com.lissdy;
public class CurrentDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private Subject weatherDate;
public CurrentDisplay(Subject weatherDate)
{
this.weatherDate=weatherDate;
weatherDate.registerObserver(this);
}
public void update(float temperature,float humidity,float pressure)
{
this.temperature=temperature;
this.humidity=humidity;
display();
}
public void display()
{
System.out.println("目前状况是温度:"+temperature+"度 "+"湿度:"+humidity+"%");
}
}
温度统计:
package com.lissdy;
public class StatisticsDisplay implements Observer,DisplayElement{
private float temperature;
private Subject weatherDate;
private float max=0;
private float min=100;
private float sum=0;
private int i=0;
public StatisticsDisplay(Subject weatherDate)
{
this.weatherDate=weatherDate;
weatherDate.registerObserver(this);
}
public void update(float temperature,float humidity,float pressure)
{
this.temperature=temperature;
i++;
sum=sum+temperature;
if(temperature>max)
{
max=temperature;
}
if(temperature<min)
{
min=temperature;
}
display();
}
public void display()
{
System.out.println("平均温度是:"+(sum/i)+"最高温度是:"+max+"最低温度是:"+min);
}
}
建立一个测试程序:
package com.lissdy;
public class WeatherStation {
public static void main(String[] args)
{
WeatherData weatherData=new WeatherData(); //建立一个WeatherData对象
CurrentDisplay currentDisplay=new CurrentDisplay(weatherData); //建立布告板,并把WeatherData传给它们
StatisticsDisplay statisticsDisplay=new StatisticsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f); //模拟新的气象测量
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
执行结果:
以上采用自己构建观察者模式的方法完成了气象站系统。但是,JAVA API内有内置的观察者模式。
java .util包内包含了最基本的Observer接口和Observable类,这和之前自己构造的Observer接口和Subject接口很相似。
若使用java内置观察者模式实现气象站系统,其设计图为:
利用内置的支持重做气象站:
首先,把WeatherData改成使用java.util.Observable
package com.lissdy;
import java.util.Observable;
public class WeatherData extends Observable {
private float temperature;
private float pressure;
private float humidity;
public WeatherData() {
} // 不需要再使用ArrayList来记录观察者了,API代劳
public void measurementsChanged() {
setChanged();
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;
}
}
重做布告板:
当前天气:
package com.lissdy;
import java.util.Observer;
import java.util.Observable;
public class CurrentDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData) obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println("目前状况是温度:" + temperature + "度 " + "湿度:"
+ humidity + "%");
}
}
气温统计:
package com.lissdy;
import java.util.Observable;
import java.util.Observer;
public class StatisticsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float max = 0;
private float min = 100;
private float sum = 0;
private int i = 0;
public StatisticsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object args) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData) obs;
this.temperature = weatherData.getTemperature();
i++;
sum = sum + temperature;
if (temperature > max) {
max = temperature;
}
if (temperature < min) {
min = temperature;
}
display();
}
}
public void display() {
System.out.println("平均温度是:" + (sum / i) + "最高温度是:" + max + "最低温度是:"
+ min);
}
}
执行结果:
注意和之前得到的结果相同,但是布告板的排列顺序不同。这是由于自己实现的观察者模式和JAVA API中的notifyObservers()方法实现方式不同造成的。
java.util.Observable是一个类而不是一个接口,违反了针对接口编程,而非针对实现编程的设计原则。
在JavaBeans和Swing中也都实现了观察者模式,例如一个按钮绑定两个监听器,按下按钮时,两个监听器都被触发。