Head First设计模式:(二)观察者模式

通过具体实现一个气象监测系统来理解观察者模式

此系统的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。

具体来说该应用需要:利用WeatherDate对象从气象站取得数据,并更新三个布告板:目前状况、气象统计和天气预报。

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

主题和观察者定义了一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格,观察者可能因此新值而更新。

关于观察者的一切,主题只知道观察者实现了某个接口(Observer接口)。主题不需要知道观察者的具体类是谁、做了些什么或其他任何细节,将对象之间的相互依赖性降到最低。符合了

设计原则:

为了交互对象之间的松耦合设计和努力。

结合气象站的需求和观察者模式的定义,得到气象站的设计图如下:

Head First设计模式:(二)观察者模式_第1张图片

从图中可以看出,有三个接口需要建立: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);
	}
}

执行结果:

Head First设计模式:(二)观察者模式_第2张图片
以上采用自己构建观察者模式的方法完成了气象站系统。但是,JAVA API内有内置的观察者模式

java .util包内包含了最基本的Observer接口和Observable类,这和之前自己构造的Observer接口和Subject接口很相似。

若使用java内置观察者模式实现气象站系统,其设计图为:

Head First设计模式:(二)观察者模式_第3张图片

利用内置的支持重做气象站:
首先,把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中也都实现了观察者模式,例如一个按钮绑定两个监听器,按下按钮时,两个监听器都被触发。

你可能感兴趣的:(设计模式)