观察者模式(Observer Pattern):定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
现实世界的模型:
根据业务模型的不同,有这几种类型的观察者:
发布-订阅模式,订阅者会订阅一些主题,在主题发生变化的时候会收到通知。
模型-视图模式,常见于 MVC、MVP 或 MVVM 架构中,特点为 View 和 Model 进行绑定,在 Model 变化的时候 View 也同步变化。
源-监听模式,常见 UI 层的交互事件的监听。比如在前端交互场景下的单击、双击、滑动、触摸、长按等等,通过设置一个或者多个监听来对事件响应。也可以对一些执行任务加入回调方法,在执行到某个阶段抛出方法。
观察者模式主要有这几个角色:
设计抽象主题类,内部维护一个观察者列表,同时提供三个基本操作:注册、注销、通知。
public class Subject {
private final List list = new ArrayList();
public void register(IObserver observer) {
list.add(observer);
}
public void unregister(IObserver observer) {
list.remove(observer);
}
public void notifyObservers() {
for (IObserver observer : list) {
observer.update();
}
}
}
具体主题类,提供接口供使用者调用,然后调用抽象主题的通知方法把变化通知给观察者们。复杂一些的主题类还会有状态等条件。
public class ConcreteSubject extends Subject {
public void change() {
notifyObservers();
}
}
抽象观察者,定义接收通知的接口:
public interface IObserver {
void update();
}
具体观察者,实现接收通知的接口:
public class ObserverA implements IObserver {
public void update() {
System.out.println("A 收到通知");
}
}
使用的时候,往主题里动态注册观察者。然后使用者直接调用主题的方法通知所有观察的观察者。
public class TestObserver {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ObserverA a = new ObserverA();
ObserverB b = new ObserverB();
ObserverC c = new ObserverC();
// 发通知
subject.change();
// A 注册
subject.register(a);
subject.register(b);
// 发通知
subject.change();
// A 注销,C 加入
subject.unregister(a);
subject.register(c);
// 发通知
subject.change();
}
}
观察者的应用频率很高。很多框架或业务设计都可以看到它的影子。可以从几个维度思考是否需要使用观察者模式:
这里举 Spring 容器事件监听的例子。
使用 Spring 时,如果需要监听 ApplicationContext 生命周期的事件,并且做出相应处理,会用到两个重要的类:
Spring 使用的是观察者模式实现外部对事件监听的。
ApplicationListener 就是观察者,定义如下:
public interface ApplicationListener extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
接受事件通知的方法为 onApplicationEvent。
主题应该是容器,但 Spring 这里做了进一步的抽象,观察者集合交给了 ApplicationEventMulticaster 来维护。所以这里的主题转为 ApplicationEventMulticaster,为一个广播发送者。
ApplicationEventMulticaster 的定义如下:
public interface ApplicationEventMulticaster {
/**
* Add a listener to be notified of all events.
* @param listener the listener to add
*/
void addApplicationListener(ApplicationListener> listener);
/**
* Add a listener bean to be notified of all events.
* @param listenerBeanName the name of the listener bean to add
*/
void addApplicationListenerBean(String listenerBeanName);
/**
* Remove a listener from the notification list.
* @param listener the listener to remove
*/
void removeApplicationListener(ApplicationListener> listener);
/**
* Remove a listener bean from the notification list.
* @param listenerBeanName the name of the listener bean to add
*/
void removeApplicationListenerBean(String listenerBeanName);
/**
* Remove all listeners registered with this multicaster.
* After a remove call, the multicaster will perform no action
* on event notification until new listeners are being registered.
*/
void removeAllListeners();
/**
* Multicast the given application event to appropriate listeners.
*
Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}
* if possible as it provides a better support for generics-based events.
* @param event the event to multicast
*/
void multicastEvent(ApplicationEvent event);
/**
* Multicast the given application event to appropriate listeners.
*
If the {@code eventType} is {@code null}, a default type is built
* based on the {@code event} instance.
* @param event the event to multicast
* @param eventType the type of event (can be null)
*/
void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}
可以看到接口主要分三种,添加监听,删除监听和向注册的监听广播事件。
ApplicationEventMulticaster 的实现类为 SimpleApplicationEventMulticaster。
在 multicastEvent 会轮询注册的监听,传递事件。
容器 AbstractApplicationContext 会调用 publishEvent
调用 ApplicationEventMulticaster 去通知消息。
比如容器刷新结束时,在 AbstractApplicationContext#finishRefresh:
protected void finishRefresh() {
...
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
...
}
事件总线一般用在进行组件间的通信和解耦。是应用的事件发布/订阅的轻量级框架。可以实现多主题与多观察者,也就是多对多通信。
使用了观察者模式,在抛出消息后,会轮询观察者列表找到注册该事件的组件,更新通知。
Android 客户端的消息总线框架有 EventBus、Otto 等。各个组件(Activity 或 Fragment)通过 EventBus 进行解耦,只需要关心事件的接收和处理。
Google 的 Guava 包也有 EventBus,可以用来实现简单的事件总线。
消息队列中间件一般用来进行解耦、异步、削峰,分布式环境下生产者消费者模型的实现。
常见的 MQ 有 ActiveMQ、RocketMQ、Kafka 等。
使用了观察者模式中的发布-订阅模型。
消息队列增加了队列来储存消息。
响应式编程官网传送门:reactivex.io
Reactive 响应式编程,事件驱动。
这里以 RxJava 框架举例。GitHub: ReactiveX/RxJava
RxJava 是一个支持响应式编程的 Java 框架。可以用来实现函数式编程、响应式编程、链式编程。
RxJava 底层就是用观察者模式实现的,可以认为是观察者的一大扩展。
创建被观察者:
// 创建被观察者
Observable observable = Observable.create(new ObservableOnSubscribe() {
public void subscribe(ObservableEmitter emitter) throws Exception {
// 发射器,发送事件
emitter.onNext(1);
emitter.onNext(2);
emitter.onNext(3);
emitter.onNext(4);
emitter.onComplete();
}
});
创建观察者:
// 创建观察者
Observer observerA = new Observer() {
private Disposable disposable;
public void onSubscribe(Disposable d) {
this.disposable = d;
System.out.println("A 订阅主题");
}
public void onNext(Integer integer) {
System.out.println("A 收到 " + integer);
}
public void onError(Throwable e) {
System.out.println("A 收到异常");
}
public void onComplete() {
System.out.println("A 结束");
// 取消订阅
disposable.dispose();
}
};
订阅:
observable.subscribe(observerA);
运行的输出:
A 订阅主题
A 收到 1
A 收到 2
A 收到 3
A 收到 4
A 结束
观察者膨胀:数量过多的观察者,整个通知过程会有明显耗时。
循环依赖:观察者和被观察主题存在循环依赖,会引起循环通知,轻则发出多个无效通知,重则系统崩溃。
编写代码时候要提高警惕,如果有迫不得已的循环依赖,要加入明确退出条件避免循环通知。