EventBus—思考观察者模式与发布订阅者模式
EventBus系列文章:
EventBus—使用实践
EventBus—源码解析
1. 概述
在EventBus的学习当中,EventBus是基于发布订阅者模式的消息处理框架。
EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose coupling.
发现发布订阅者模式与观察者模式有极大相似之处,而且部分博客将两者等同起来。其实两者是存在差异。两者差异在于调度方式不同。
首先必须知道,发布订阅者模式和观察者模式都是处理对象间一对多关系的行为设计模式,当一个对象状态发生了变化,依赖它的对象都会收到变化和更新,
2. 观察者模式
2.1. 观察者模式UML类图
观察者模式有以上两种实现:一种将被观察者方法抽象出来,一种直接创建被观察者对象,以图一讲述观察者模式,其包含四个角色:
- Observerable:被观察者抽象接口,提供注册和解注册以及通知观察者的抽象方法;
- ConcreteObserverable:具体被观察者对象,维护了一个观察者集合对象并实现所需的通知观察者的接口;
- Observer:观察者抽象接口,定义更新观察者自己的更新方法;
- ConcreteObserver:具体的观察者对象,实现更新的业务逻辑;
2.2. 具体例子
- 定义被观察者抽象接口
public interface Observerable {
void regiseted(Observer observer);
void unregiseted(Observer observer);
void notifyObserver();
}
- 定义具体被观察者对象
public class ConcreteObserverable implements Observerable{
private ArrayList<Observer> mObservers = new ArrayList<>();
@Override
public void regiseted(Observer observer) {
mObservers.add(observer);
}
@Override
public void unregiseted(Observer observer) {
if (mObservers.contains(observer)) {
mObservers.remove(observer);
}
}
@Override
public void notifyObserver() {
for (Observer observer : mObservers) {
observer.update();
}
}
public void updateState(){
Log.i("wuzl", "update state , the state has changed.");
notifyObserver();
}
}
- 定义观察者抽象接口
public interface Observer {
void update();
}
- 定义具体观察者对象:ObserverA,ObserverB,ObserverC
public class ObserverA implements Observer {
@Override
public void update() {
Log.i("wuzl", "Observer A receives the update event");
}
}
public class ObserverB implements Observer {
@Override
public void update() {
Log.i("wuzl", "Observer B receives the update event");
}
}
public class ObserverC implements Observer {
@Override
public void update() {
Log.i("wuzl", "Observer C receives the update event");
}
}
- 客户端调用:
public void verifyObserverPattern(){
Observer observerA = new ObserverA();
Observer observerB = new ObserverB();
Observer observerC = new ObserverC();
ConcreteObserverable observerable = new ConcreteObserverable();
observerable.regiseted(observerA);
observerable.regiseted(observerB);
observerable.regiseted(observerC);
observerable.updateState();
}
- 结果输出
wuzl: update state , the state has changed.
wuzl: Observer A receives the update event
wuzl: Observer B receives the update event
wuzl: Observer C receives the update event
(以上代码均为测试验证代码,非实际开发代码(手动苦笑))
2.3. 小结
观察者模式很好地解耦对象间一对多的关系,并且建立起一套触发更新的机制。但是通过上面的例子发现,被观察者(ConcreteObserverable)的对象太重了,既要维护观察者的列表,又要处理消息通知(既当爹又当妈),这违背设计模式中单一职责模式,一旦业务逻辑复杂起来将会酿成灾难呀。同时如果有多个被观察者类(ConcreteObserverable),每个被观察者触发更新逻辑基本一致,重复实现造成大量冗余的垃圾代码。
那么发布订阅者模式应运而生,将观察者的维护、消息处理逻辑剥离出来构建一个调用中心,通过构建事件触发观察者的更新。
3. 发布订阅者模式
讲述发布订阅者模式前,先看看EventBus官网的结构图:
其结构图就是发布订阅者模式的结构图。
3.1. 发布订阅者模式UML图
该模式有五个角色:
- Publisher:发布者,发布事件告知调度中心“事件更新了”;
- Dispatcher:调度中心,类似观察者模式中“被观察者”的功能,维护事件与订阅者的映射关系列表,以及处理订阅、发布等逻辑事件,一般单例对象;
- Event:事件对象;
- Subscriber:订阅者,与观察者模式的抽象观察者一致;
- ConcreteSubscriber:具体的订阅者,与观察者模式的观察者一致;
3.2. 具体例子
- 构建调度中心(Dispatcher):单例+subscriptionByEventType映射表格+typesBySubscriber映射表格。
subscriptionByEventType:存储了订阅者与事件的映射关系表(一个订阅者可以订阅多个事件);
typesBySubscriber:存储了事件与订阅者的映射关系表(一个事件可以对应多个订阅者)
public class Dispatcher {
private static Dispatcher mInstance;
private Dispatcher(){}
public static Dispatcher getInstance(){
if (mInstance == null) {
synchronized (Dispatcher.class) {
if (mInstance == null) {
mInstance = new Dispatcher();
}
}
}
return mInstance;
}
private HashMap<Class<?>, List<Subscriber>> subscriptionByEventType = new HashMap<Class<?>, List<Subscriber>>();
private HashMap<Subscriber, List<Class<?>>> typesBySubscriber = new HashMap<Subscriber, List<Class<?>>>();
public void registered(Subscriber subscriber, Class event) {
List<Subscriber> subscribers;
if (!subscriptionByEventType.containsKey(event)) {
subscribers = new ArrayList<Subscriber>();
} else {
subscribers = subscriptionByEventType.get(event);
}
subscribers.add(subscriber);
subscriptionByEventType.put(event, subscribers);
}
public void unregistered(Subscriber subscriber) {
List<Class<?>> classes = typesBySubscriber.get(subscriber);
if (classes == null) {
return;
}
for (Class event : classes) {
if (subscriptionByEventType.containsKey(event)) {
subscriptionByEventType.remove(event);
}
}
}
public void post(Class event) {
List<Subscriber> subscribers = subscriptionByEventType.get(event);
if (subscribers != null && !subscribers.isEmpty()) {
for (Subscriber subscriber : subscribers) {
subscriber.onEvent();
}
}
}
}
- 构建订阅者抽象类
public interface Subscriber {
void onEvent();
}
- 具体的抽象类
public class SubscriberA implements Subscriber {
@Override
public void onEvent() {
Log.i("wuzl", "SubscriberA onEvent");
}
}
public class SubscriberB implements Subscriber {
@Override
public void onEvent() {
Log.i("wuzl", "SubscriberB onEvent");
}
}
public class SubscriberC implements Subscriber {
@Override
public void onEvent() {
Log.i("wuzl", "SubscriberC onEvent");
}
}
- 发布者与事件
public class Publisher {
public void publishNewEvent(){
Log.i("wuzl", "publishNewEvent");
Dispatcher.getInstance().post(NewEvent.class);
}
public void verifyPublisherPattern(){
Subscriber subscriberA = new SubscriberA();
Subscriber subscriberB = new SubscriberB();
Subscriber subscriberC = new SubscriberC();
Dispatcher.getInstance().registered(subscriberA, NewEvent.class);
Dispatcher.getInstance().registered(subscriberB, NewEvent.class);
Dispatcher.getInstance().registered(subscriberC, NewEvent.class);
publishNewEvent();
}
}
public class NewEvent {
}
- 结果输出
wuzl: publishNewEvent
wuzl:SubscriberA onEvent
wuzl:SubscriberB onEvent
wuzl:SubscriberC onEvent
3.3. 小结
我大大简化了发布订阅者模式的代码,方便例子输出,EventBus中的实现更加的人性化与解耦性更好,详情可查看EventBus—源码解析。
从上面的发布订阅者模式的例子,调度中心维护了观察者事件列表、消息处理逻辑,职责更加单一并且逻辑更加清楚,然后通过事件驱动整个订阅模式的流转。
4. 总结
- 观察者模式和发布订阅者模式都是用于处理对象间一对多的关系;
- 两个模式都是为了解耦代码、建立一套更新机制;
- 观察者模式中被观察者对象背负了过重的逻辑:观察者列表和消息处理;
- 订阅发布者模式剥离出单例的调用中心,以事件作为驱动;