设计模式这本书中对观察者模式做了如下定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。下面我对深入浅出设计模式这本书中的观察者模式,进行自己的理解性剖析。先贴代码,然后对每段代码进行解析,最后画出完整的类图。观察者模式有两个必须的要素就是主题和观察者。在书中,引用了气象站的例子。我们有一个气象站(获取气象数据),有一个weatherData用来收集气象站的数据,并通知布告板做出更新。还有一个布告板系统用来显示收集到的信息。
public interface Subject {
public void registerObserver(Observer o);
}
我们需要定义一个主题接口,体现了面向接口的编程,而不是具体的实现。然后我们需要接着定义观察者的接口
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
这里需要说明一下主题中,需要有一个方法证明哪些观察者需要被通知,哪些不需要,所以就需要有一个注册方法,类似于一个论坛,你只有注册了,才能享受会员资格。而所有的观察者中就要有一个更新的方法,一旦发生改变就要更新。
当我们定义一个主题的实现类,用来收集气象站的数据。
import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
//构造方法中声明了一个ArrayList用来记录所有注册的观察者
public WeatherData() {
observers = new ArrayList();
}
//将观察者加入到其中就是注册
public void registerObserver(Observer o) {
observers.add(o);
}
//删除
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}
//通知,从ArrayList中取出数据后调用更新方法
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
//有更新
public void measurementsChanged() {
notifyObservers();
}
//将更新的数据传出,并告知有更新
public void setMeasurements(float temperature, float humidity,
float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
// WeatherData的其他方法
}
我们有许许多多的观察者,在此例中我们有许许多多的显示面板,需要显示不同的信息,我们定义了三种不同的面板用以显示不同的信息,每一个具体的实现都要保留一个主题的引用。
public class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
public class ForecastDisplay implements Observer,DisplayElement{
private float humidity;
private Subject weatherData;
public ForecastDisplay(Subject weatherData) {
this.weatherData = weatherData;
((WeatherData) weatherData).registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.humidity=humidity;
display();
}
@Override
public void display() {
if(this.humidity<=65.0){
System.out.println("ForeCast : Improving weather on the way!");
}else if(this.humidity<=80){
System.out.println("ForeCast : Watch out for cooler,rainy weather!");
}else{
System.out.println("ForeCast : More of the same!");
}
}
}
public class StatisticsDisplay implements Observer,DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public StatisticsDisplay(Subject weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver( this);
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display() {
if(this.humidity<=65.0){
System.out.println("Avg/Max/Min temperature = 80.0/80.0/80.0");
}else if(this.humidity<=80){
System.out.println("Avg/Max/Min temperature = 81.0/82.0/80.0");
}else{
System.out.println("Avg/Max/Min temperature = 80.0/82.0/78.0");
}
}
}
都实现了observer接口和displayelement接口,以方便weatherdata在具体实现的调用,体现了面向接口编程的优越性。
下面我们要写具体的气象站类用来促使整个过程的发生。
public class WeatherStation {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay =new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
这样开始我们的神奇之旅吧。我们在每次调用一个观察者的时候就要进行注册,而注册是在构造函数中进行的,所以需要这么写。发动数据只要进行数据的更新操作即可。