观察者模式:定义对象之间一对多的依赖,当一个对象状态改变会通知它所有依赖者自动更新。
我的理解: 这种模式类似于订阅通知功能,你是一个博主,你的关注者订阅了你,当你发布新的博客时,会发布通知告诉所有订阅者,你发新博客了。
观察者模式类图:
HeadFirst 案例分析:
气象站希望公布一组API,其他的开发者可以根据API开发出不同的气象布告板,气象数据会随时变更,要保证数据更新了同时通知所有布告板更新数据。
根据上图发现,不管多少个布告板,WeatherData这个对象都不会变,所以它在观察者模式一对多中的Subject
,而布告板可以有多个,显而易见它就是Observer
自定义实现Subject/Observer
Subject 接口定义注册、删除以及通知观察者的接口方法
/**
* @author zhaotianxin
* @date 2021-07-20 20:25
* 定义Subject接口,支持注册、删除、通知观察者
*/
public interface Subject {
// 注册观察者
void registerObserver(Observer observer);
// 删除观察者
void removeObserver(Observer observer);
// 通知观察者
void notifyObservers();
}
Observer 接口 提供更新接口
/**
* @author zhaotianxin
* @date 2021-07-20 20:27
*/
public interface Observer {
void update(float temp, float humidity, float pressure);
}
WeatherData 类实现Subject接口, 并实现存储、注册、删除、通知观察者的逻辑
/**
* @author zhaotianxin
* @date 2021-07-20 20:30
* 天气数据类,实现Subject接口
*/
public class WeatherData implements Subject{
// 用于存储所有订阅此消息的观察者
private List<Observer> observers;
private float temp;
private float humidity;
private float pressure;
public WeatherData() {
observers = new ArrayList<>();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int index = observers.indexOf(observer);
if(index > 0 ){
observers.remove(observer);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temp, humidity, pressure);
}
}
/**
* 数据发生变化调用此方法,通知观察者
*/
public void dataChange(float temp, float humidity, float pressure){
this.temp =temp;
this.humidity =humidity;
this.pressure =pressure;
notifyObservers();
}
WeatherApp : 根据WeatherData 的数据实现的一个 应用或者显示板,当前模式中的观察者。通过实例化时传入Subject 去实现注册。这种可以有多个
/**
* @author zhaotianxin
* @date 2021-07-20 20:37
* 观察者1
*/
public class WeatherApp implements Observer {
private float temp;
private float humidity;
private float pressure;
private Subject weatherData;
/***
* 初始化实例时,传入Subject,将当前设置成观察者
* @param subject
*/
public WeatherApp(Subject subject){
this.weatherData = subject;
subject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("观察者1数据更新:" +temp + "-" + humidity + "--"+ pressure);
}
}
/**
* @author zhaotianxin
* @date 2021-07-20 20:37
* 观察者2
*/
public class SmallWeatherApp implements Observer {
private float temp;
private float humidity;
private float pressure;
private Subject weatherData;
/***
* 初始化实例时,传入Subject,将当前设置成观察者
* @param subject
*/
public SmallWeatherApp(Subject subject){
this.weatherData = subject;
subject.registerObserver(this);
}
@Override
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display(){
System.out.println("观察者2数据更新:" + temp + "-" + humidity + "--"+ pressure);
}
}
调用实现:
public class ObserverMain {
public static void main(String[] args) {
// 初始化Subject
WeatherData subject = new WeatherData();
// 注册第一个观察者
WeatherApp weatherApp = new WeatherApp(subject);
// 注册第二个观察者
SmallWeatherApp smallWeatherApp = new SmallWeatherApp(subject);
// 发送第一波数据
System.out.println("---------------第一次数据更新--------------------------------");
subject.dataChange(11F,244,56F);
// 数据更新,并发送到观察者
System.out.println("---------------第二次数据更新--------------------------------");
subject.dataChange(11F,254,568F);
// 观察者2不订阅了,准备跑路了
subject.removeObserver(smallWeatherApp);
//这时数据更新,只会发送给观察者一
System.out.println("---------------第三次数据更新--------------------------------");
subject.dataChange(15F,345,134F);
}
}
执行结果
Java内置的观察者模式(Observable/Observer)
java在java.util包下提供内置的观察者模式,提供了Observable
类和Observer
接口分别对应上面的Suject
和Observer
Observable 定义了addObserver
、deleteObserver
、notifyObservers
、notifyObservers
等方法,其中有个setChange()方法,只有changed 为true才会通知观察者
改造WeatherData
WeatherData中不需要保存observers,Observable类已经有实现了。修改dataChange方法
public void dataChange(float temp, float humidity, float pressure){
this.temp =temp;
this.humidity =humidity;
this.pressure =pressure;
setChanged();
notifyObservers();
}
改造WeatherApp
实现java.util包下的Observer接口,修改原来的update方法
@Override
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData) obs;
this.temp = weatherData.getTemp();
this.humidity = weatherData.getHumidity();
this.pressure = weatherData.getPressure();
display();
}
}
setChange方法的作用
setChange的作用是便于控制,现实中除非对要求数据非常高,不可能一点数据变动就通知观察者,setChange让你想发送的才放松,这是可以控制的。比如:但温度提高了3°就发送通知。
使用内置观察者模式存在的问题
首先,细心的人应该会发现,Observable 的通知顺序和上面自定义的是不同的。这样意味着当观察者/可观察者的实现逻辑发生变化,通知次序就发生变化,这样就不符合松耦合了。
其次,Observable 是一个类,而不是接口,这会限制代码的复用潜力,setChange方法是受保护的意味着只有直接继承自Observable,否则你没办法创建Observable实例