(本文中一些例子和定义均摘自《Head First 设计模式》)
“观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态的时候,它的所有依赖者都会收到通知并自动更新。”
举例,天气情况报告。
我们有很多不同的表示天气预报的公告栏,当天气情况发生变化的时候,这些公告栏都能够接收到变化的通知,并且自动的根据变化而变化。
当观察者不需要再订阅主题的时候,能够退订主题。这样天气预报变化的情况就不会再通知它。
步骤:
1. 主题接口
public
interface
Subject
...
{
//注册观察者
public void registerObserver(Observer o);
//注销观察者
public void removeObserver(Observer o);
//通知所有的观察者
public void notifyObservers();
}
2. 观察者接口

public interface Observer ...
{
//对主题的变化情况,做更新
public void update(float temperature, float humidity, float pressure);
}
3. 显示接口
public
interface
DisplayElement
...
{
//观察者通过此接口来显示信息
public void display();
}
4. 对具体的主题,实现主题接口
import
java.util.ArrayList;


public
class
WeatherData
implements
Subject
...
{
@SuppressWarnings("unchecked")
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
@SuppressWarnings("unchecked")

public WeatherData() ...{
observers = new ArrayList();
}
@Override

public void notifyObservers() ...{

for(int i = 0; i < observers.size(); i++) ...{
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}

}

@SuppressWarnings("unchecked")
@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);
}

}

public void measurementsChanged() ...{
notifyObservers();
}

public void setMeasurements(float temperure, float humidity, float pressure) ...{
this.temperature = temperure;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}

}
5. 建立布告栏,也就是我们的观察者,实现观察者接口
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);
}
@Override

public void update(float temperature, float humidity, float pressure) ...{
this.temperature = temperature;
this.humidity = humidity;
//display();

}

@Override

public void display() ...{
System.out.println("Current conditions: " + temperature + " F degree and " + humidity + " % humidity");

}

}
这样整个基本的模型就搭建完成。接下来测试一下
public
class
WeatherStation
...
{

public static void main(String[] args) ...{
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(100, 80, 90);
currentDisplay.display();
}
}
可以发现,当主题(weatherData)发生变化的时候,观察者(currentDisplay)收到变化的通知,并且随着主题的变化而变化。
JAVA内置的观察者模式
一个被观测的对象必须服从下面的两个简单规则。第一,如果它被改变了,它必须调用setChanged( )方法。第二,当它准备通知观测程序它的改变时,它必须调用notifyObservers( )方法。这导致了在观测对象中对update( )方法的调用。注意——当对象在调用notifyObservers( )方法之前,没有调用setChanged( )方法,就不会有什么动作发生。在update( )被调用之前,被观测对象必须调用setChanged( )和notifyObservers( )两种方法
Observable中有两个重载的方法,一个是不带参数的notifyObservers(),一个是带参数的notifyObservers(Object arg)
先说那个带参数的notifyObservers(Object arg):
这个参数Object arg 其实就是 Observer接口中的update(Observable o, Object arg)方法中的第二个参数
其实就是一个数据对象,也就是通知观察者,改变的数据对象是什么
这就是一种PUSH的方法,由主题主动的PUSH需要改变的数据对象给观察者
例子:
public
class
WeatherData
extends
Observable
...
{
private float temperature;
private float humidity;
private float pressure;

……..

public void measurementsChanged() ...{
setChanged();
notifyObservers(float temperature);
}
}
public
class
CurrentDisplay
extends
Observer
...
{
private float temperature;
private float humidity;
private float pressure;

……..

public void update(Observable obs,Object arg) ...{

if(arg instanceof Float)...{
temperature = ((Float)arg).floatValue();
}
}
而第二种不带参数的notifyObservers(),当调用它的时候, 传递一个null的数据对象给观察者.
其实也就是说,观察者需要改变什么数据,是需要观察者自己到主题那里去pull.
也就是说,通知你主题发生了变化,但是具体需要什么变化的数据,由你自己决定.
例子:
public
class
WeatherData
extends
Observable
...
{
private float temperature;
private float humidity;
private float pressure;

……..

public void measurementsChanged() ...{
setChanged();
notifyObservers();
}

public float getTemperature()...{
Return temperature;
}
}
public
class
CurrentDisplay
extends
Observer
...
{
private float temperature;
private float humidity;
private float pressure;

……..

public void update(Observable obs,Object arg) ...{

if(obs instanceof WeatherData)...{
WeatherData weatherData = (WeatherData)obs;
This.temperature = weatherData.getTemperature();
}
}
JAVA内置观察者模式不足之处:
Observable是一个类,这样的话就必须要继承它才能实现主题的功能。当你想从一个超类中继承实现这个功能就不可能了。并且Observable将关键方法setChanged()给保护起来了,也就是说如果你不继承Observable就无法创建实例到自己的对象中。