(本文中一些例子和定义均摘自《Head First 设计模式》)
“观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态的时候,它的所有依赖者都会收到通知并自动更新。”
举例,天气情况报告。
我们有很多不同的表示天气预报的公告栏,当天气情况发生变化的时候,这些公告栏都能够接收到变化的通知,并且自动的根据变化而变化。
当观察者不需要再订阅主题的时候,能够退订主题。这样天气预报变化的情况就不会再通知它。
步骤:
1. 主题接口
public
interface
Subject
...
{
//注册观察者
public void registerObserver(Observer o);
//注销观察者
public void removeObserver(Observer o);
//通知所有的观察者
public void notifyObservers();
}
2. 观察者接口
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public interface Observer ...
{
//对主题的变化情况,做更新
public void update(float temperature, float humidity, float pressure);
}
3. 显示接口
public
interface
DisplayElement
...
{
//观察者通过此接口来显示信息
public void display();
}
4. 对具体的主题,实现主题接口
import
java.util.ArrayList;
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
public
class
WeatherData
implements
Subject
...
{
@SuppressWarnings("unchecked")
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
@SuppressWarnings("unchecked")
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public WeatherData() ...{
observers = new ArrayList();
}
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void notifyObservers() ...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
for(int i = 0; i < observers.size(); i++) ...{
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
@SuppressWarnings("unchecked")
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void registerObserver(Observer o) ...{
observers.add(o);
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void removeObserver(Observer o) ...{
int i = observers.indexOf(o);
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(i <= 0) ...{
observers.remove(i);
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void measurementsChanged() ...{
notifyObservers();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void setMeasurements(float temperure, float humidity, float pressure) ...{
this.temperature = temperure;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
5. 建立布告栏,也就是我们的观察者,实现观察者接口
public
class
CurrentConditionsDisplay
implements
Observer, DisplayElement
...
{
private float temperature;
private float humidity;
private Subject weatherData;
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public CurrentConditionsDisplay(Subject weatherData) ...{
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void update(float temperature, float humidity, float pressure) ...{
this.temperature = temperature;
this.humidity = humidity;
//display();
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
@Override
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void display() ...{
System.out.println("Current conditions: " + temperature + " F degree and " + humidity + " % humidity");
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
}
这样整个基本的模型就搭建完成。接下来测试一下
public
class
WeatherStation
...
{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
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;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
……..
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void measurementsChanged() ...{
setChanged();
notifyObservers(float temperature);
}
}
public
class
CurrentDisplay
extends
Observer
...
{
private float temperature;
private float humidity;
private float pressure;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
……..
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void update(Observable obs,Object arg) ...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
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;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
……..
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void measurementsChanged() ...{
setChanged();
notifyObservers();
}
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public float getTemperature()...{
Return temperature;
}
}
public
class
CurrentDisplay
extends
Observer
...
{
private float temperature;
private float humidity;
private float pressure;
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
……..
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
public void update(Observable obs,Object arg) ...{
![](https://i-blog.csdnimg.cn/blog_migrate/37c8bf68cdc3cc81759c34160776bc53.gif)
if(obs instanceof WeatherData)...{
WeatherData weatherData = (WeatherData)obs;
This.temperature = weatherData.getTemperature();
}
}
JAVA内置观察者模式不足之处:
Observable是一个类,这样的话就必须要继承它才能实现主题的功能。当你想从一个超类中继承实现这个功能就不可能了。并且Observable将关键方法setChanged()给保护起来了,也就是说如果你不继承Observable就无法创建实例到自己的对象中。