概述
观察者模式是对象间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知bserver变化。也就是开闭原则
例子
需求
- 气象站可以每天测量到温度,湿度,气压等以公告的形式发布出去。
- 需要设计开放型api,便于其他第三方也可以接入气象台获取数据
- 提供温度,湿度,气压的接口
- 测量数据更新时,实时通知第三方
传统思维
创建一个WeatherData模式气象台的数据,该类维护一个观察者对象,对对象进行操作
/*核心类
* 1、含有最新的天气情况信息
* 2、含有CurrentConditions对象
* 3、当数据有更新时,就主动的调用 CurrentConditions对象update方法,这样接入方就可以看到最新的消息
*
* 缺点:无法在运行时动态添加第三方,添加第三方非常麻烦,违反了开闭原则
* */
public class WeatherData {
private float temperatrue;
private float pressure;
private float humidity;
private CurrentConditions currentConditions;//维护的观察者对象
public WeatherData(CurrentConditions currentConditions){
this.currentConditions = currentConditions;
}
public float getTemperatrue(){
return temperatrue;
}
public float getPressure(){
return pressure;
}
public float getHumidity(){
return humidity;
}
public void dataChange(){
currentConditions.update(temperatrue,pressure,humidity);
}
//数据更新时调用setData
public void setData(float temperatrue,float pressure,float humidity){
this.temperatrue = temperatrue;
this.pressure = pressure;
this.humidity = humidity;
//推送消息给接受方
dataChange();
}
}
创建观察者对象
public class CurrentConditions {
//模拟数据
private float temperature;//温度
private float pressure;//压强
private float humidity;//湿度
//提供数据更新接口,便于气象台管理数据更新
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
public void display(){
System.out.println("**今天天气的温度为"+temperature+"***");
System.out.println("**今天天气的压强为"+pressure+"***");
System.out.println("**今天天气的湿度为"+humidity+"***");
}
}
客户端代码
public class Client {
public static void main(String[] args) {
//创建接入方currentConditions
CurrentConditions currentConditions = new CurrentConditions();
//创建WeatherData,并将CurrentCondition传递到WeatherData中
WeatherData weatherData = new WeatherData(currentConditions);
//更新天气信息
weatherData.setData(30,140,40);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新信息
System.out.println("============天气更新=============");
weatherData.setData(45,150,23);
}
}
传统模式的缺点:气象台weatherData只维护对应的观察者对象,当添加新的第三方接口调用的时候,维护比较困难,需要修改WeatherData中的代码,无法做到实时动态跟新。
观察者模式改进
我们通过实现接口的方式统一管理观察者,在weatherData中维护一个该接口的观察者列表,当数据更新的时候,循环列表统一通知第三方接口。在weatherData中提供注册和删除接口,实现动态添加观察者。
Subject接口设计
//import java.util.Observer;
//接口,让WeatherData来实现
public interface Subject {
public void registerObver(Observer o);
public void removeObver(Observer o);
public void notifyObservers();
}
WatherData实现类
/*核心类
* 1、含有最新的天气情况信息
* 2、含有观察者集合,使用ArrayList管理
* 3、当数据有更新时,就主动的调用 ArrayList对象update方法,通知接入方获取最新的消息
*
* 缺点:无法在运行时动态添加第三方,添加第三方非常麻烦,违反了开闭原则
* */
public class WeatherData implements Subject{
private float temperatrue;
private float pressure;
private float humidity;
private ArrayList<Observer> observers;//观察者对象列表
public WeatherData(){
observers = new ArrayList<Observer>();
}
public float getTemperatrue(){
return temperatrue;
}
public float getPressure(){
return pressure;
}
public float getHumidity(){
return humidity;
}
public void dataChange(){
//currentConditions.update(temperatrue,pressure,humidity);
notifyObservers();//调用通知所有消息到第三方接口
}
//数据更新时调用setData
public void setData(float temperatrue,float pressure,float humidity){
this.temperatrue = temperatrue;
this.pressure = pressure;
this.humidity = humidity;
//推送消息给接受方
dataChange();
}
//添加一个观察者
@Override
public void registerObver(Observer o) {
observers.add(o);
}
//移除一个观察者
@Override
public void removeObver(Observer o) {
observers.remove(o);
}
//遍历所有观察者,并通知
@Override
public void notifyObservers() {
for (Observer ob: observers
) {
ob.update(getTemperatrue(),getPressure(),getHumidity());
}
}
}
观察者类接口,所有观察者都必须继承该接口,WeatherDate聚合了该接口
//观察者接口,由观察者实现
public interface Observer {
public void update(float temperture,float pressure,float humidity);
}
观察者实现类
public class CurrentConditions implements Observer{
//模拟数据
private float temperature;//温度
private float pressure;//压强
private float humidity;//湿度
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
public void display(){
System.out.println("** currentcondition温度为"+temperature+"***");
System.out.println("**currentcondition的压强为"+pressure+"***");
System.out.println("**currentcondition的湿度为"+humidity+"***");
}
}
public class Baidu implements Observer{
//模拟数据
private float temperature;//温度
private float pressure;//压强
private float humidity;//湿度
public void update(float temperature,float pressure,float humidity){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
display();
}
//显示
public void display(){
System.out.println("** baidu温度为"+temperature+"***");
System.out.println("**baidu的压强为"+pressure+"***");
System.out.println("**baidu的湿度为"+humidity+"***");
}
}
在客户端进行观察者的注册和更改数据
public class Client {
public static void main(String[] arg) {
//创建一个Weather
Subject weatherData = new WeatherData();
//创建观察者
CurrentConditions currentConditions = new CurrentConditions();
//创建另一个观察者
Baidu baidu = new Baidu();
//注册到WeatherData
weatherData.registerObver(currentConditions);
weatherData.registerObver(baidu);
//测试
System.out.println("通知各个注册的观察者。看看信息");
((WeatherData) weatherData).setData(10f,100f,30.3f);
//取消注册
weatherData.removeObver(currentConditions);
//再次测试
System.out.println("通知各个注册的观察者。看看信息");
((WeatherData) weatherData).setData(20f,123f,12.3f);
}
}
使用观察者模式,我们将观察者抽象成接口的实现,方便管理,这样当我们需要添加新的第三方接口调用时,只需要继承Observer接口,并在客户端进行注册便可,不需要修改WeatherData代码。这符合开闭原则,提高了项目的扩展性。
观察者模式在JDK中的实现
Observable类根据观察者模式,与以上的例子类似。