概述
观察者模式,又叫发布/订阅模式(Publish-Subscribe)。定义对象之间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于他的对象都会收到通知并且执行对应的更新逻辑。
在日常生活中也有很多也有很多观察者模式的场景,比如我们订阅某个电视剧,当电视剧有更新的时候,我们会收到通知。这里观察者对象就是我们用户,被观察者对象就是电视剧。
程序案例
我们把上面电视剧的订阅通过代码实现一下。
- 定义观察者对象Observer
抽象出观察者对象,有很多不同类别的具体观察者对象,比如学生,上班族,家庭妇女等。
public interface TVObserver {
/**
* 处理
* @param message 消息
*/
void process(String message);
}
具体的学生观察者对象
public class StudentTvObserver implements TvObserver{
@Override
public void process(String message) {
System.out.println("我是学生," + message + ", 我要翘课追剧了");
}
}
具体的打工人观察者对象
public class WorkerTvObserver implements TvObserver {
@Override
public void process(String message) {
System.out.println("我是打工人," + message + ", 我只能下班看了");
}
}
- 定义被观察对象Subject
定义抽象的被观察对象或者主题,因为电视剧其实有很多,比如《梦华录》,《士兵突击》等,都需要单独订阅。
public abstract class Tv {
private String tvName;
private List<TvObserver> tvObservers = new ArrayList<>();
//增加订阅者
public void subscribe(TvObserver observer) {
tvObservers.add(observer);
};
//取消订阅
public void unsubscribe(TvObserver observer) {
tvObservers.remove(observer);
}
//通知订阅者更新消息
public void notify(String message) {
tvObservers.forEach(item -> {
item.process(message);
});
}
public String getTvName() {
return tvName;
}
public void setTvName(String tvName) {
this.tvName = tvName;
}
}
定义具体的被观察者的对象,也就是你要订阅的电视剧《梦华录》
public class MengHuaLuTv extends Tv {
public MengHuaLuTv() {
this.setTvName("梦华录");
}
@Override
public void notify(String message) {
System.out.println("梦华录大结局了,开始推送给大家");
super.notify(message);
}
}
执行测试
public class Test {
public static void main(String[] args) {
Tv tv = new MengHuaLuTv();
TvObserver studentOb = new StudentTvObserver();
tv.subscribe(studentOb);
WorkerTvObserver workerTvObserver = new WorkerTvObserver();
tv.subscribe(workerTvObserver);
// 通知
tv.notify("梦华录大结局啦");
}
}
优缺点
优点
降低目标与观察者对象之间的耦合关系。
被观察者发送通知,所有注册的观察者收到通知,处理对应的逻辑,实现广播机制。
缺点
如果观察者对象很多的话,那么所有观察者收到通知的时间会很长
如果观察者对象的处理逻辑写的存在问题,会级联影响其他观察者对象收到通知的时间。
适用场景
对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
当多个对象存在依赖关系,但是你不想要他们之间耦合,可以考虑适用观察者模式。
更多案例
jdk中的观察者模式
在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
Observer 接口
Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 update 方法,进行相应的工作。
- Observable类
Observable 类是抽象目标类(被观察者),它有一个 Vector 集合成员变量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法。
- void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
- void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
- void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
实际上,我们可以把上面的电视剧订阅的例子可以直接用jdk中的接口实现。
Zookeeper中的Watch机制
Zookeeper中也有类似的观察者模式,就是Watch机制,zk客户端向服务端注册监听某个节点或者节点中的数据,如果节点的路径或者数据发生变化,便会收到来时zk服务端的通知,执行相应的处理逻辑。
Springboot的监听器
Springboot中的监听器机制,本质上也是观察者模式的衍生。
- 监听器(ApplicationListener)对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应逻辑。
- 事件发布器(ApplicationEventMulticaster)对应于观察者模式中的被观察者/主题, 负责通知观察者(监听器) 对外提供发布事件和增删事件监听器的接口,维护事件和事件监听器之间的映射关系,并在事件发生时负责通知相关监听器。
- 事件(ApplicationEvent)负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因。
参考
https://java-design-patterns.com/patterns/observer/
https://github.com/alvinlkk/design_pattern/blob/main/doc/observer.md