定义
观察者模式(Observer Pattern)也叫做发布订阅模式(Publish/subscribe)。定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。
观察者模式是一种对象行为型模式。
类图
Subject被观察者:定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责:管理观察者并通知观察者。
Observer观察者:观察者接收到消息后,即进行update(更新方法)操作,对接收到的信息进行处理。
ConcreteSubject具体的被观察者:定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。
ConcreteObserver具体的观察者:每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。
优缺点
优点
- 观察者和被观察者之间是抽象耦合,降低耦合;
- 目标与观察者之间建立了一套触发机制,事件发生触发观察者动作;
- 支持广播通信。
缺点
运行效率与开发效率,一个被观察者,多个观察者,开发与调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。
应用
Spring 事件驱动模型是观察者模式很经典的应用。也就是常见的事件监听器。
事件:ApplicationEvent 是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject
, 所有的事件都需要继承 ApplicationEvent
, 还要传入事件源作为构造参数。
事件监听:ApplicationListener,也就是观察者,继承自 JDK 的 EventListener,该类中只有一个方法 onApplicationEvent
。当监听的事件在容器中被发布,该方法将被调用。同时,该接口是一个泛型接口,其实现类可以通过传入泛型参数指定该事件监听器要对哪些事件进行监听。
事件源:ApplicationContext,ApplicationContext
是 Spring 中的核心容器,ApplicationContext
接口继承了 ApplicationEventPublisher
接口,从而提供了对外发布事件的能力。在 ApplicationEventPublisher
中定义了事件发布的方法:publishEvent(Object event)
事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
实现
我们应该都用过微信公众号,我们就用公众号的文章发布与订阅者的消息接收作为示例:
首先定义微信公众号接口作为被观察者:
public interface WeChatOfficialAccount {
void registerSubscriber(WeChatOfficialAccountSubscriber subscriber);
void removeSubscriber(WeChatOfficialAccountSubscriber subscriber);
void notifySubscriber(String message);
}
定义一个具体的公众号,它持有订阅者对象:
public class MyWeChatOfficialAccount implements WeChatOfficialAccount{
private List<WeChatOfficialAccountSubscriber> subscriberList = new ArrayList<>();
@Override
public void registerSubscriber(WeChatOfficialAccountSubscriber subscriber) {
subscriberList.add(subscriber);
}
@Override
public void removeSubscriber(WeChatOfficialAccountSubscriber subscriber) {
subscriberList.remove(subscriber);
}
@Override
public void notifySubscriber(String message) {
subscriberList.forEach(subscriber -> subscriber.articleUpdate(message));
}
}
定义微信公众号订阅者接口:
public interface WeChatOfficialAccountSubscriber {
void articleUpdate(String message);
}
定义微信公众号的具体订阅者也就是普通的用户,持有name属性标识是什么人
public class WeChatUser implements WeChatOfficialAccountSubscriber {
private String name;
public WeChatUser(String name) {
this.name = name;
}
@Override
public void articleUpdate(String message) {
System.out.println("接收到了新的文章通知,我要开始阅读了!" + message);
}
}
定义client:
public class Client {
public static void main(String[] args) {
WeChatOfficialAccountSubscriber user1 = new WeChatUser("chenly");
WeChatOfficialAccountSubscriber user2 = new WeChatUser("Stone");
WeChatOfficialAccount officialAccount = new MyWeChatOfficialAccount();
//两个微信用户订阅了这个公众号
officialAccount.registerSubscriber(user1);
officialAccount.registerSubscriber(user2);
//公众号发布文章,订阅者都会受到文章通知
officialAccount.notifySubscriber("论持久战.....");
//Stone取消订阅
officialAccount.removeSubscriber(user2);
//再次发布文章
officialAccount.notifySubscriber("如何学习英语.....");
}
}
输出:
接收到了新的文章通知,我要开始阅读了!论持久战.....
接收到了新的文章通知,我要开始阅读了!论持久战.....
接收到了新的文章通知,我要开始阅读了!如何学习英语.....
注意事项
广播链的问题:根据经验建议在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的。注意它和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。
异步处理问题:被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?可以使用异步,异步处理就要考虑线程安全和队列的问题,Spring里有健全的异步消息执行机制有兴趣可以研究。
参考
《设计模式之禅》
https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html#id10