观察者模式
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
举例
现在有一个气象站,需要发布发布天气,气象站只提供数据,需要编写程序将天气发布给每一个用户,用户接收也可能有多种模式,有的是当前天气状况也有的是天气预报。
这时候就可以使用观察者模式,进行一对多推送,观察者模式主要就是订阅、发布过程,用户订阅了天气信息,气象站对用户及时发布最新的天气信息,用户也可以随时取消订阅。
如图
- Subject接口: 主题接口,也就是被观察对象的接口
- Observer接口: 观察者接口
- DisplayElement接口: 展示接口
- WeatherData: 具体的被观察对象
- CurrentConditionDisplay: 当前天气状态的展示
通过上图,就可以看出观察者模式的运行情况了,具体主题实现Subject接口,具体展示实现Observer和DisplayElement接口,Subject中有注册、移除、通知三个方法,这样就可以对观察者进行注册移除,并通知观察者。
// 模拟天气站,发布天气
public class WeatherStation
{
public static void main(String[] args)
{
WeatherData weatherData = new WeatherData();
CurrentConditionDisplay current = new CurrentConditionDisplay(weatherData);
weatherData.setMeasurement(35, 65, 30.4f);
weatherData.setMeasurement(36, 67, 30.5f);
weatherData.setMeasurement(34, 66, 30.5f);
// 移除观察者
current.remove();
weatherData.setMeasurement(33, 62, 30.5f);
}
}
输出结果
当前状态: 温度 35.0度 湿度 65.0%
当前状态: 温度 36.0度 湿度 67.0%
当前状态: 温度 34.0度 湿度 66.0%
这样就基本完成了气象站的需求,可以对观察者们发布最新的天气信息。
Java Jdk中内置的观察者模式实现
- Observable类: 对应上述的Subject,也就是被观察的对象要实现的接口
- Observer接口: 对应上述的Observer接口,观察者实现的接口
使用内置的对象来实现观察者模式整体上跟上述基本相同,但是Observable是一个类,是需要用继承方式来扩展。
在Observable中,有一个setChanged()方法,在通知之前,需要调用这个方法,来表明状态已经改变了。
在Observer中的update()方法有两个参数,一个是一个Observable对象,还有一个就是Observable对象“推”的一个对象。在jdk内置方法中,发布有两种形式,一种是"推",一种是"拉",在Observable类中的notifyObservers()方法里,可以带一个参数,代表是"推",推送给每一个观察者,也可以不带参数,就默认是"拉"方式,这样就变得更为灵活。
但是Observable是一个类,而不是一个接口,这样在程序扩展性上也有了一些限制,如果可以满足需求,就可以直接使用,如果不行,那么只能自己写一套观察者模式出来,就像最前面那样。
使用jdk中观察者的类
import java.util.Observable;
// 使用jdk提供的内置对象完成观察者模式
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;
}
}
import ObserverPattern.DisplayElement;
import java.util.Observable;
import java.util.Observer;
// 展示当前状态
public class CurrentConditionDisplay implements Observer, DisplayElement
{
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionDisplay(Observable observable)
{
this.observable = observable;
observable.addObserver(this);
}
public void remove()
{
observable.deleteObserver(this);
}
public void update(Observable obs, Object arg)
{
if(obs instanceof WeatherData)
{
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display()
{
System.out.println("当前状态: 温度 " + temperature + "度 " + " 湿度 " + humidity + "%");
}
}
以上具体代码都可以在我的github中查看
地址:https://github.com/yangliu0/DesignPatterns