原文出处:http://www.cnblogs.com/gnuhpc/archive/2012/12/17/2822398.html
1.我们需要理解报社、订阅系统和订报人之间的关系,订报人通过订阅系统订报,一旦报社有新的报纸,订阅系统就会派人送或者邮寄给订报人新的报纸。然后,出版者+订阅者就是观察者模式,只不过名称不一样,主题(Subject,或者叫做Observabler)类比于出版者,是事件发生的主体,订阅者改称为观察者(Observer),是响应事情发生的主体。该模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新,这样的模式让主题和观察者之间松耦合。它体现了第四个原则:为了交互对象之间的松耦合设计而努力。
2.要建立一个气象站--气象搜集装置--多个类型的气象发布版的一个系统:
我们首先定义两个接口,实现这两个接口的类就是相应的主题类--气象搜集装置以及观察者类--对应气象发布版:
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
在主题接口中,我们要求有三个方法:注册、注销、通知观察者。可以看到,我们都是在针对接口编程
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
在观察者中,我们要求有一个方法:更新气象发布版的数据。
另外,我们还需要一个辅助的接口进行显示更新的数据,这就是所谓把操作相分离的动作解耦思想:
public interface DisplayElement {
public void display();
}
我们先根据主题接口实现气象搜集装置:
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的封装
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();//这个方法会进而去通知所有的观察者
}
// other WeatherData methods here
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
我们再根据观察者接口实现多种气象发布版,这里只举出一个例子,其余大同小异:
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);//创建的时候就注册,这里也可以不进行这个,而使用 WeatherData 的registerObserver公有方法
}
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);
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);
}
}
请注意,这里主机主要是采用“推”的方式进行数据传送的。总结一下:
主题是事件发生的单位,观察者是对发生的事件进行响应的单位。那么很自然的,对事件是否发生感兴趣的可能有多个观察者,那么一个主题内部就有一个列表维护观察者;同时,观察者要指明对什么事件感兴趣(registerObserver),所以观察者内部要有一个主题的实例,当然若这个观察者不再对这个事件感兴趣,也可以使用这个主题的实例完成(unregisterObserver)。
在事件发生时,主题会通知列表上的观察者,并且可以通过参数传数据完成数据推送(另一种方式是主题提供接口供观察者调用,也就是所谓“拉”的方式),观察者收到事件发生的通知(update)时做出相应动作。
最后再举一个例子说明这个模式的应用,在界面编程中,一个button往往会注册一个onclickListener,里面有onclickAction方法对点击进行响应,此时button充当的就是主题,而onclickListener就是观察者,而onclickAction就对应着观察者中对事件进行响应的update方法。
UML图:
3.Java内置的观察者模式
我们使用Java内置的观察者模式再实现一遍气象站的项目(省略导入适当的包的过程):
public class WeatherData extends Observable {//直接继承了Java提供的主题超类,注意这里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;
}
}
我们现在再创建一个使用Java内置观察者模式完成的气象发布版:
public class CurrentConditionsDisplay implements Observer , DisplayElement {//注意这里Observer是一个接口
Observable observable;//主题的引用,用于注册、注销等操作
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);//这里进行了注册,也可以使用
}
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {//判断是不是属于WeatherData类
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();//这里体现了拉的方式
this.humidity = weatherData.getHumidity();
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 currentConditions = 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);
}
}
内置的观察者模式有一定的明显缺陷,首先它是一个类,这个就与我们的第一个原则不符合,若一个类想以观察者模式完成要从另一个继承来的功能就会陷入两难。而且它将关键方法setChanged()设置为protected,这就意味着你必须继承Observable,否则无法创建Observable实例并组合到你设计的对象中。
相关推荐
HeadFirst 设计模式学习笔记2--观察者模式 demo http://blog.csdn.net/laszloyu/archive/2010/05/12/5581769.aspx
HeadFirst设计模式学习笔记比较全面详细地讲解了13个设计模式,有利于大家更好的学习HeadFirst设计模式,希望亲们会喜欢~~~
Head First 设计模式学习笔记。更多内容请参见文章内容。
经典的设计模式书籍打包:Head First 设计模式 +Java设计模式(第2版)
笔记_HeadFirst设计模式
HeadFirst 设计模式学习笔记3--装饰模式 Demo http://blog.csdn.net/laszloyu/archive/2010/05/12/5582561.aspx
Head First 设计模式(高清中文完整版带目录)+附书源码+HeadFirst设计模式学习伴侣.rar 又名: Head First Design Patterns 作者: (美)弗里曼(Freeman,E.) 副标题: Head First Design Patterns 简介 ·····...
http://blog.csdn.net/laszloyu/archive/2010/05/11/5579765.aspx 示例代码
如果你曾经读过任何一本深入浅出(Head First)系列书籍,你就会知道能够从本书中得到的是:透过丰富的视觉效果让你的大脑充分地运作。本书的编写运用许多最新的研究,包括神经生物学、认知科学以及学习理论,这使得...
head first 设计模式 观察者模式 C++ 代码
Head First设计模式, 中国电力出版社出版,英文书名:Head First Design Patterns...信耶稣的人都要读圣经,信OO的人都要读四人组的《设计模式》,而《设计模式》的原创者Erich Gamma欣然推荐《Head First 设计模式》。
Head First设计模式(完整高清版).pdf,涵盖了各种宝贵的设计思想!
head first 设计模式 高清中文版 pdf
HeadFirst设计模式学习伴侣.jpg
Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First 设计模式Head First...
HeadFirst设计模式 读书 笔记
Head.First 设计模式学习笔记.pdf Head.First 设计模式学习笔记.pdf
Head First设计模式 Head First设计模式 Head First设计模式
Head First设计模式中文版 Head First设计模式中文版 Head First设计模式中文版
Head First 设计模式(高清中文完整版带目录)+附书源码+HeadFirst设计模式学习伴侣.rar 又名: Head First Design Patterns 作者: (美)弗里曼(Freeman,E.) 副标题: Head First Design Patterns 简介 ·····...