楔子
event一种我们在代码中经常使用的对象。我们通过一定的方式创建出一个event,然后调用上下文将event发送出去。对应的listener就会收到消息并执行响应的逻辑。
我们都知道这种基于“事件”的开发中,用到设计模式就是观察者模式(也叫发布订阅模式),那么什么是观察者模式呢?
观察者模式
观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时通知多个“观察”该对象的其他对象。
事件中心demo
在观察者模式中有三个要素:
- 主题,也就是我们的事件中心
- 观察者,众多listener
- 被观察的对象,各种event
好了,定义我们已经了解了,那么如何使用观察者模式实现一个事件中心呢?
被观察者
首先我们定一下被观察者,也就是我们的事件对象
/**
* 事件
* @author skyline
*/
public class BaseEvent {
private final Object data;
public BaseEvent(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
}
具体的事件
从事件基类中继承而来的一个具有业务场景的具体事件。
/**
* 到家了事件
* @author skyline
*/
public class GetHomeEvent extends BaseEvent {
public GetHomeEvent(String userName) {
super(userName);
}
}
观察者
接下来我们定义一下观察者
/**
* 事件监听者
* @author skyline
*/
public interface IEventListener<T extends BaseEvent> {
/**
* 监听,当有事件到来时,这个方法会被调用
* @param event 事件
*/
void listener(T event);
}
具体事件的具体观察者
/**
* 空气净化器对回家监听
*/
public class AirCleanerGetHomeListener implements IEventListener<GetHomeEvent> {
private static final Logger logger = LoggerFactory.getLogger(AirCleanerGetHomeListener.class);
@Override
public void listener(GetHomeEvent event) {
logger.info("[{}]回来了,空气净化器已打开", event.getData());
}
}
/**
* wifi对回家监听
*/
public class WifiGetHomeListener implements IEventListener<GetHomeEvent> {
private static final Logger logger = LoggerFactory.getLogger(WifiGetHomeListener.class);
@Override
public void listener(GetHomeEvent event) {
logger.info("WiFi已自动连接到[{}]的手机", event.getData());
}
}
主题
主题,也就是我们的事件中心。事件中心有两大基本能力,注册和发布
/**
* 事件中心
* @author skyline
*/
public interface IEventCenter {
/**
* 发布事件
* @param event
*/
void publishEvent(BaseEvent event);
/**
* 注册事件
* @param eventListener
*/
void registerEvent(IEventListener<? extends BaseEvent> eventListener);
}
具体的主题—>事件中心
/**
* 事件中心
*
* @author skyline
*/
public enum EventCenter implements IEventCenter {
/**
* 单例
*/
INSTANCE;
private final Map<Class, List<IEventListener>> listenerMap = new HashMap<>();
@Override
public void publishEvent(BaseEvent event) {
//根据event的类型获取对应的listener
List<IEventListener> eventListeners = listenerMap.get(event.getClass());
if (eventListeners != null) {
eventListeners.forEach(eventListener -> eventListener.listener(event));
}
//如果发出的event不是BaseEvent,那就给所有对BaseEvent感兴趣的listener都发送一下消息
if (!event.getClass().equals(BaseEvent.class)) {
List<IEventListener> listeners = listenerMap.get(BaseEvent.class);
if (listeners != null) {
listeners.forEach(eventListener -> eventListener.listener(event));
}
}
}
@Override
public void registerEvent(IEventListener<? extends BaseEvent> eventListener) {
//建立起Listener感兴趣的Event类型与Listener的映射关系
Class genericInterface = (Class) ((ParameterizedType) eventListener.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
List<IEventListener> eventListeners = listenerMap.computeIfAbsent(genericInterface, k -> new ArrayList<>());
eventListeners.add(eventListener);
}
}
验证事件中心
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
IEventCenter eventCenter = EventCenter.INSTANCE;
eventCenter.registerEvent(new AirCleanerGetHomeListener());
eventCenter.registerEvent(new WifiGetHomeListener());
//不指定具体的event类型,就是对所有事件都感兴趣
eventCenter.registerEvent(new IEventListener<BaseEvent>() {
@Override
public void listener(BaseEvent event) {
logger.info("eventType[{}] getMessage [{}]", event.getClass().getSimpleName(), event.getData());
}
});
eventCenter.publishEvent(new GetHomeEvent("张三"));
eventCenter.publishEvent(new BaseEvent("李四"));
}
}
好了,我们看下结果:
22:47:49.464 [main] INFO com.example.designpatterns.observer.AirCleanerGetHomeListener - [张三]回来了,空气净化器已打开
22:47:49.464 [main] INFO com.example.designpatterns.observer.WifiGetHomeListener - WiFi已自动连接到[张三]的手机
22:47:49.464 [main] INFO com.example.designpatterns.observer.Main - eventType[GetHomeEvent] getMessage [张三]
22:47:49.464 [main] INFO com.example.designpatterns.observer.Main - eventType[BaseEvent] getMessage [李四]
完全没有问题
总结
- 观察者模式定义了对象之间的一对多关系,比如在
com.example.designpatterns.observer.EventCenter#listenerMap
中存放了事件与监听器的一对多关系。 - 主题(也可能就是观察者)用一个共同的接口来更新观察者。就好像EventCenter调用
com.example.designpatterns.observer.IEventListener#listener
来触发事件一样。 - 当存在多个观察者时,不可以依赖特定的通知顺序。
- 面向接口开发,解耦利器。