观察者模式
定义:定义一种一对多的依赖关系,一个主题对象可以被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并自动更新
适用场景:
- 当一个或者多个对象的变化依赖于另一个对象的变化
- 需要实现类似广播的功能,无需知道具体收听者,只需要分发广播,系统中感兴趣的对象会自动接收该广播
- 多层嵌套使用,形成一种链式触发机制,使得事件具备跨域(跨越两种观察者类型)通知
优点:
- 观察者和被观察者是松耦合的,只是在抽象层面耦合,符合依赖倒置原则
- 实现了一对多的通讯机制,支持事件注册机制,支持兴趣分发机制
缺点:
- 如果观察者数量过多,则事件通知会耗时较长
- 事件通知呈线性关系,如果其中一个观察者处理时卡住,则影响后续观察者接收该事件
- 如果观察者和被观察者之间存在依赖关系,有可能因为循环引用导致系统奔溃
示例
JDK 实现
JDK 默认有提供观察者模式的实现,以论坛提问为例
问题对象,被观察者通知观察者的消息载体
java
public class Question {
private String userName;
private String content;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}论坛对象,被观察对象
java
public class Forum extends Observable {
private String name = "论坛";
private static final Forum forum = new Forum();
private Forum() {}
public static Forum getInstance(){
return forum;
}
public String getName() {
return name;
}
public void publishQuestion(Question question){
System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
setChanged();
notifyObservers(question);
}
}用户对象,观察者对象,需要实现 Observer 中的 update() 方法,表示收到通知后做出的反应
java
public class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
public void update(Observable o, Object arg) {
Forum forum = (Forum)o;
Question question = (Question)arg;
System.out.println("======================");
System.out.println(name + "你好!\n" +
"您收到了一个来自" + gper.getName() + "的提问,希望您解答。问题内容如下:\n" +
question.getContent() + "\n" +
"提问者:" + question.getUserName());
}
}客户端使用,发出用户行为前需要先把观察者 注册 到被观察者对象中。当问题发布后,会自动通知到 tom 和 jerry 用户并调用 update 方法
java
public class Test {
public static void main(String[] args) {
Forum forum = Forum.getInstance();
User tom = new User("Tom");
User jerry = new User("Jerry");
forum.addObserver(tom);
forum.addObserver(jerry);
//用户行为
Question question = new Question();
question.setUserName("张三");
question.setContent("观察者模式适用于哪些场景?");
forum.publishQuestion(question);
}
}Guava 实现
Guava 这个组件库中也提供了非常方便易用的观察者模式的解决方案
先导入依赖
xml
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>创建侦听事件
java
public class GuavaEvent {
// 订阅通知
@Subscribe
public void subscribe(String str) {
System.out.println("执行subscribe方法: " + str);
}
}客户端使用
java
public class Test {
public static void main(String[] args) {
// 事件总线
EventBus eventBus = new EventBus();
GuavaEvent guavaEvent = new GuavaEvent();
// 注册侦听的事件
eventBus.register(guavaEvent);
// 发送通知
eventBus.post("test");
}
}观察者模式在源码中的体现
SpringMVC
SpringMVC 中的 ContextLoaderListener 实现了 Servlet中的 ServletContextListener 接口,而 ServletContextListener 接口是继承 JDK 中的 EventListener。 ContextLoaderListener 主要的作用是在程序启动时加载 Spring 上下文 ApplicationContext
java
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener() {
}
public ContextLoaderListener(WebApplicationContext context) {
super(context);
}
public void contextInitialized(ServletContextEvent event) {
this.initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
this.closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}