一、含义
在对象之间定义一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象都会收到通知,并自动更新。
二、设计原则
1.封装变化。
2.多用组合,少用继承。
3.针对接口编程,不针对实现编程。
4.为交互对象之间的松耦合设计而努力。
三、要点
1.观察者模式定义了对象之间一对多的关系。
2.主题(也就是可观察者)用一个共同的接口来更新观察者。
3.观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
4.使用此模式时,你可从被观察者处(push)或拉(pull)数据(然而,推的方式被认为更“正确”)。
5.有多个观察者时,不可以依赖特定的通知次序。
三、实战分析策略模式
需求:根据气象站获得的数据(包括温度,湿度,其它),实时显示在不同的布告板上(现在是三套布告板)。
我们首先设计一个WeatherData类,来获得气象测试数据。
需求分析:我们需要三个使用天气数据的布告板,一但WeatherData有新的测量,这些布告必须马上更新,并且此系统应该需要可扩展,可以让其它开发人员可以自定义布告板,并且用户可以随心所欲的添加和删除布告板。
其实观察模式就是:出版者,可称为主题(subject)+订阅者,可称为观察者(observer)。
从图中可以看到,主题和观察者定义了一对多的关系。观察者依赖此主题,只要主题状态一有改变,观察者就会被通知。根据通知的风格,观察者可能因此新值而进行更新。
具体类图设计如下
松耦合的威力就是当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
四、代码演示
1.先定义接口
/**
* 主题实现的接口
*/
public interface Subject {
/**
* 添加观察者
* @param o
*/
public void registerObserver(Observer o);
/**
* 删除观察者
* @param o
*/
public void removeObserver(Observer o);
/**
* 当主题状态改变时,这个方法调用,通知所有观察者
*/
public void notifyObservers();
}
/**
* 所有观察者必须实现的接口
*/
public interface Observer {
/**
* 跟新显示数据
* @param temp
* @param humidity
* @param pressure
*/
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {
/**
* 当布告板需要显示时,调用此方法
*/
public void display();
}
2.主题具体的实现类
/**
* WeatherData实现主题接口
*/
public class WeatherData implements Subject {
/** 用来存放观察者 */
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers = new ArrayList();
}
@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 = (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.观察者具体的实现类
/**
* 某一个布告板的具体实现
*/
public class CurrentConditionsDisplay implements Observer,DisplayElement {
private float temperature;
private float humidity;
private Subject weatherData;
/**
* 构造器需要将weatherData对象(也就是主题)作为注册用。
* @param weatherData
*/
public CurrentConditionsDisplay(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 temp, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}
}
4.编写测试类
public class WeatherStationTest {
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentConditionsDisplay =
new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(70,65,22.5f);
weatherData.setMeasurements(78,68,20.1f);
weatherData.setMeasurements(88,79,21.1f);
}
}
5.运行结果
Current conditions: 0.0F degrees and 65.0% humidity
Current conditions: 0.0F degrees and 68.0% humidity
Current conditions: 0.0F degrees and 79.0% humidity
五、扩展思维
在上面的观察者模式实现过程中,观察者只能等待主题去推送消息,而不能自己去拉数据。其实在内置的java观察者模式中,分为两种,一个是主题向观察者推送消息,一个是观察者去拉消息。
拉消息的类图如下所示:
主题在发送消息时,先调用setChanged()方法,标记状态已经改变的事实。然后调用两种notifyObservers()方法中的一个。
具体代码实现如下:
主题的实现
/**
* WeatherData实现主题接口
*/
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
/**
* 构造器中不在需要为了记住观察者们而建立数据结构了
*/
public WeatherData() {
}
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;
}
}
观察者的实现代码:
/**
* 某一个布告板的具体实现
*/
public class CurrentConditionsDisplay implements Observer,DisplayElement {
Observable observable;
private float temperature;
private float humidity;
/**
* 将对象注册为观察者
* @param observable
*/
public CurrentConditionsDisplay(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();
}
}
}
运行结果如上。