前段时间在工作中使用了观察者模式来做代码段解耦工作,依赖的是通过消息时间处理,但是由于使用的是第三方的依赖,所以将消息的订阅和发布给屏蔽了一部分。今天特意来深入了解一下这方面的知识内容点。
对于观察者来说,必定会有事件源和观察事件源的观察者。
简单来说,观察者模式就是多个观察者关注同一个事件源,当事件源发生改动的时候,多个观察者都能立马获取到响应。
通常我们可以将这种关系用下图来表示:
对于设计的代码里面来说,通常我们会定义一个叫做事件源的模块,并且在让观察者和事件源之间为了能有更好的关联,
内部一般都会注入一个source字段。
常见的观察者模式都会用到以下这些类名:
observer listener hook callback 复制代码
所以以后如果你见到了这些参数,也请不用太过懵逼,其实也就那样罢了。
首先我们通过一段案例来大致认识一下如何借助观察者模式对我们的代码进行优化处理。
假定场景:会员付费进行升级和会员退费进行降级:
首先是相关的eventobject
/** * 事件源头 因为消息的发送在接收端是需要根据参数判断的,所以一般都会封装一个source字段 * * @Author idea * @Date created in 10:25 上午 2020/7/11 */ public class EventObject<T> { private long time; private EventTypeEnum typeEnum; private T source; public EventObject(T object, EventTypeEnum eventTypeEnum, long time) { this.source = object; this.typeEnum = eventTypeEnum; this.time = time; } public EventObject(T object, EventTypeEnum eventTypeEnum) { this.time = System.currentTimeMillis(); this.typeEnum = eventTypeEnum; this.source = object; } public T getSource() { return source; } public EventTypeEnum getTypeEnum(){ return typeEnum; } } 复制代码
定义了事件源之后,通常,我们需要设计一个监听器。借助监听器的帮忙,可以帮助我们实现对于事件源的观察。相关案例代码如下所示:
/** * @Author idea * @Date created in 9:00 上午 2020/7/11 */ public interface ApplicationListener { /** * 绑定一个事件 * * @param eventObject */ void bind(EventObject eventObject); } 复制代码
我这里定义了一个监听器的接口,名字为UpdateListener。其对应的一个实现类如下代码所示。:
/** * @Author idea * @Date created in 10:30 上午 2020/7/11 */ public class UpdateListener implements ApplicationListener { @Override public void bind(EventObject eventObject) { if (eventObject == null) { return; } //如果是升级事件,那么可以做对应的处理 if (eventObject.getTypeEnum().equals(UPGRADE_TYPE)) { MsgObj msgObj = (MsgObj) eventObject.getSource(); msgObj.print(); } if (eventObject.getTypeEnum().equals(DEGRADE_TYPE)) { MsgObj msgObj = (MsgObj) eventObject.getSource(); msgObj.print(); } } } 复制代码
你可以清晰地看到在这个监听器里面,有一个名字为bind的方法,这个方法会根据入参的event object里面的type参数去识别发送过来的事件源是属于哪一类型。
关于message object代码如下所示:
/** * @Author idea * @Date created in 10:39 上午 2020/7/11 */ public class MsgObj { String msg; public MsgObj(String msg) { this.msg = msg; } public void print() { System.out.println("========" + msg + "======="); } } 复制代码
这部分的代码非常简单,只是将信息给打印出来。
这里面我们为了模拟真实场景。特地定义了两个枚举对象,内部存储了不同的消息事件,代码如下:
/** * @Author idea * @Date created in 10:32 上午 2020/7/11 */ public enum EventTypeEnum { UPGRADE_TYPE(1, "升级监听器"), DEGRADE_TYPE(2,"降级事件"); EventTypeEnum(int code, String des) { this.code = code; this.des = des; } int code; String des; } 复制代码
最后便是我们的会员对象。根据会员在函数里面执行升级或者降级,然后发送相关的事件原给到对应的监听者,监听者做出相关的回应。相关代码如下:
/** * @Author idea * @Date created in 10:23 上午 2020/7/11 */ public class Member { private int id; private String name; private boolean isVip; public Member(String name) { this.name = name; } public void upgrade(ApplicationListener applicationListener) { MsgObj msgObj = new MsgObj(name+"会员充钱了,升级"); EventObject eventObject = new EventObject(msgObj, EventTypeEnum.UPGRADE_TYPE); applicationListener.bind(eventObject); } public void degrade(ApplicationListener applicationListener) { MsgObj msgObj = new MsgObj(name+"会员退费了,降级"); EventObject eventObject = new EventObject(msgObj, EventTypeEnum.DEGRADE_TYPE); applicationListener.bind(eventObject); } } 复制代码
编写对应的一个测试类,在其main函数里面注入相关的监听器。
/** * @Author idea * @Date created in 10:52 上午 2020/7/11 */ public class Test { public static void main(String[] args) { ApplicationListener applicationListener = new UpdateListener(); Member member = new Member("idea"); member.degrade(applicationListener); member.upgrade(applicationListener); } } 复制代码
这个时候,一个简单的观察者模式案例代码就已经完成了。但是我们在实际的工作中,观察者模式并不仅仅只有这么简单。它还可以做到点对点,广播等各种复杂的玩法。之前我在工作中有尝试过用观察者模式来对代码进行解耦的一些相关操作,使用的是Google的一套第三方依赖。这套依赖的技术关键名词为eventbus,相关链接地址:
这套第三方的依赖在工作中我用的比较多的是他的一个异步编程模型,也就是说当我的观察者去观察这个事件源的时候可以做出同步的处理,或者说异步的处理。
在国外我有看到过微软也有提供类似这种第三方依赖服务,专门用于做事件源和这种观察者之间的关联,其内部运用了jd k1.8所提供了一个异步对象CompletableFuture ,相关
的地址我已经粘贴在了文末。:
最后感谢您的阅读。