本程序改编自《Head First Design Patterns》上关于气象站的例子,我将例子进行了化简。
总共7个java源文件
一个Subject接口
一个Observer接口
一个DisplayElement接口
一个Subject具体类,实现Subject接口
两个Observer具体类,实现Observer和DisplayElement接口
一个Main类,用于测试
The Observation Pattern defines a one-to-many dependency。这里的one就是Subject, many就是Observers;one就是发布者,many就是订阅者;one就是事件源,many就是监听者。
package observer;
public interface Subject
{
public void registerObserver(Observer o);
public void removeOvserver(Observer o);
public void notifyObservers();
}
package observer;
public interface Observer
{
public void update(double temperature, double humidity, String condition);
}
这个接口只有display( )一个抽象方法,其实如果放在Observer里面的话,仅从代码层面考虑的话,也可以。但是如果从逻辑层面考虑的话,最好还是分开。毕竟这个display和观察者是”两码事“,不要耦合在一起。《Head First Design Pattern》上面的例子代码这样写,是为了描绘出一个生动形象的”气象站“的例子,来帮助大家理解观察者模式。
package observer;
public interface DisplayElement
{
public void display();
}
package observer;
import java.util.ArrayList;
// Subject收集天气信息,然后通知Observers
public class WeatherData implements Subject {
private ArrayList<Observer> observers;
private double temperature;
private double humidity;
private String condition;
public WeatherData()
{
observers = new ArrayList<Observer>();
}
@Override
public void registerObserver(Observer o)
{
observers.add(o);
}
@Override
public void removeOvserver(Observer o)
{
int i = observers.indexOf(o);
if(i>=0)
{
observers.remove(i);
}
}
@Override
public void notifyObservers()
{
for(Observer observer : observers)
{
observer.update(temperature, humidity, condition);
}
}
public void measurementsChanged()
{
notifyObservers();
}
public void setMeasurements(double temperature, double humidity, String condition)
{
this.temperature = temperature;
this.humidity = humidity;
this.condition = condition;
measurementsChanged();
}
public double getTemperature()
{
return temperature;
}
public double getHumidity()
{
return humidity;
}
public String getPressure()
{
return condition;
}
}
package observer;
// 显示当前天气情况
public class CurrentDisplay implements Observer, DisplayElement {
private double temperature;
private double humidity;
private String condition;
public CurrentDisplay(Subject weatherData)
{
weatherData.registerObserver(this);
}
@Override
public void display()
{
System.out.println("Current Conditions: "+ temperature + " C degrees and "
+ humidity + "% humidity " + condition );
}
@Override
public void update(double temperature, double humidity, String condition)
{
this.temperature = temperature;
this.humidity = humidity;
this.condition = condition;
display();
}
public String toString()
{
return "This is Current Condition Display";
}
}
package observer;
// 显示历史统计数据
public class StatisticDisplay implements Observer, DisplayElement {
private double maxTemperature = 0;
private double minTemperature = 100;
private double temperatureSum = 0;
private int totalUpdating = 0;
public StatisticDisplay(Subject weatherData)
{
weatherData.registerObserver(this);
}
@Override
public void display()
{
System.out.println("Avg/Max/Min temperture: " + temperatureSum/totalUpdating + "/"
+ maxTemperature + "/" + minTemperature);
}
@Override
public void update(double temperature, double humidity, String condition)
{
temperatureSum += temperature;
totalUpdating++;
if(temperature > maxTemperature)
{
maxTemperature = temperature;
}
if(temperature < minTemperature)
{
minTemperature = temperature;
}
display();
}
public String toString()
{
return "This is Statictic Display";
}
}
package observer;
public class Main
{
public static void main(String[] args)
{
WeatherData weatherStation = new WeatherData(); // 一个气象站作为Subject
CurrentDisplay currentDisplay = new CurrentDisplay(weatherStation); // 注册Observer1
System.out.println(currentDisplay); // 这个是为了不让eclipse报出warning
StatisticDisplay statisticDisplay = new StatisticDisplay(weatherStation); // 注册Observer2
System.out.println(statisticDisplay); // 这个是为了不让eclipse报出warning
weatherStation.setMeasurements(20, 30, "晴天"); // 气象站测量出了新的天气,在第一时间通知所有的观察者
weatherStation.setMeasurements(16, 50, "多云");
weatherStation.setMeasurements(12, 60, "大风");
weatherStation.setMeasurements(12, 90, "小雨");
weatherStation.setMeasurements(11, 98, "大雨");
}
}
直接从eclipse复制过来
This is Current Condition Display
This is Statictic Display
Current Conditions: 20.0 C degrees and 30.0% humidity 晴天
Avg/Max/Min temperture: 20.0/20.0/20.0
Current Conditions: 16.0 C degrees and 50.0% humidity 多云
Avg/Max/Min temperture: 18.0/20.0/16.0
Current Conditions: 12.0 C degrees and 60.0% humidity 大风
Avg/Max/Min temperture: 16.0/20.0/12.0
Current Conditions: 12.0 C degrees and 90.0% humidity 小雨
Avg/Max/Min temperture: 15.0/20.0/12.0
Current Conditions: 11.0 C degrees and 98.0% humidity 大雨
Avg/Max/Min temperture: 14.2/20.0/11.0
这个观察者模式,多个观察者(Observer)“围观”一个被观察者(Subject),被观察者是万众瞩目的焦点。观察者如果想得到Subject的“观察权”,首先需要向Subject 申请注册,如果惹Subject生气了,不让你观察了,就把你踢开。Subject一旦发生了变化,就立刻通知所有观察者,引起观察者的更新。
黑体字就是Subject接口中的三个和Observer接口中的一个关键的抽象方法,必须实现:
申请注册:registerObserver()
踢开 :removeObserver()
通知 :notifyObservers()
更新 :update()
值得注意的是:update()方法传递的参数,可以传递整个subject对象,也可以只传递部分有用的数据成员。实验楼网站课程中的例子就是传递整个subject对象,《Head First Design Pattern》就是传递部分有用的数据成员。无论无何,最关键的是:
Subject对象中的数据成员发生改变
→ 触发Subject.notifyObservers()方法
→ 触发所有Observer.update() 方法。
万变离不开这个链条。
Java中有一个java.util.Observable类(被观察者,Subject);还有一个java.util.Observer接口(观察者,Obserer)。看来观察者模式很常用,以至于都写进到java类库里面去了。
更多设计模式,在新标签页中打开这里