您的位置:首页 > 其它

Head First Design Patterns 阅读笔记之二: Observer Pattern

2016-08-30 15:37 519 查看

从一款天气预报 APP 开始

假设现在需要编写一款 APP,可以显示感应器传来的当前天气条件(温度、湿度、气压),报告当前的天气状况统计数据并做简单的预测。现在已知以下条件:

WeatherData 类有三个 getter 方法来获取当前的数据:getTemperature()、getHumidity()、getPressure()。

WeatherData 类中有一个方法 measurementsChanged() ,每当新的天气数据获得的时候就被调用。

我们需要利用当前的数据显示上面提到的三个方面,并且在数据更新时需要更新显示内容。

系统必须易扩展,其他开发者可以添加新的显示项。

初稿完成?

根据要求写下如下代码:

public class WeatherData
{
// instance variable declarations
public void measurementsChanged()
{
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();

currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
// other WeatherData methods here
}


这样看上去对,但是存在很多问题:

- 由于 currentConditionDisplay 几个类都是具体实现的,所以动态添加其他显示选项,完全不可能。

- temp、humidity、pressure 这些会变化的地方最好使用接口,由设计原则可知,我们需要封装会变化的地方。

观察者模式

首先看一下杂志订阅的过程:

杂志出版商开始出版杂志。

你订阅一家杂志,每当它退出新刊物,就会送给你。只要你保持订阅,就始终会获得新的杂志。

一旦你停止订阅,就不会再给你送杂志。

只要杂志出版商还存在,就会有人订阅或取消订阅。

观察者模式可以用如下公式定义:

Publishers + Subscribers = Observer Pattern

在观察者模式中,我们把出版商叫做 Subject,订阅者叫做Observer。正式定义如下:

The Observer Pattern defines a one-to-many dependency between objects so that when one objet changes state, all of its dependents are notified and updated automatically.

在后面,你会发现有不同的方式实现观察者模式,但是大多数都以设计包含 Subject 和 Observer 接口的类为核心。一般的设计如下图:



松耦合的好处

如果两个对象是松耦合的,它们互相交流但是彼此了解很少。观察者模式使得 subject 和 observer 是松耦合的。为什么?

subject 只知道 observer 实现了某一个接口,所以我们可以在任意时刻增加或删除新的 observer。

增加新的 observer 类型时,我们不需要修改 subject。

我们可以独立的重用 subject 和 observer,它们当中一个的改变并不会影响另一个。

这里需要提到一个新的设计原则

在有联系的对象之间追求松耦合。

Strive for loosely coupled designs between objects that interact.

修改后的版本

画出相应的设计图:



// 几个接口
public interface Subject
{
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}

public interface Observer
{
public void update(float temp, float humidity, float pressure);
}

public interface DisplayElement
{
public void display();
}


// 实现 Subject
import java.util.ArrayList;

public class WeatherData implements Subject
{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;

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);
}
}

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();
}
// other WeatherData methods here
}


// 实现 Observer
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 WeatherStation
{
public static void main(String[] args)
{
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);

weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}


一般信息都是 observer 用 pull 的方式获取的,当然也可以使用 push 方式(内置的观察者模式可以实现),但是我们一般认为 pull 方式更好。

利用 Java 内置的观察者模式

Java API 中拥有内置的观察者模式:java.util 包中的 Observable 类和 Observer 接口。它们分别对应于前面的 Subject 和 Observer 接口。使用情况如下:



现在利用内置的 API 重写程序:





但是直接使用内置的 API 存在问题:

observable 是一个类,而不是接口

observable 中一些关键方法的访问修饰符是 protected。所有必须是子类才能调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式