行为型设计模式:观察者模式
观察者模式(发布订阅模式),在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会自动收到通知。
现在有一个注册后发送欢迎使用的功能,实现如下:
public class UserService{ private SendMessageService sendMessageService; // 依赖注入 public Long register(String telephone, String password) { //注册行为 ...
//发消息 senMessageService.sendHellow(userId); return userId; } }
这个接口就干了两件事 注册 发消息,如果一辈子需求不变,这么写是可以的,但是实际并不会一辈子都不变,那怎么办呢?发完消息还要干别的,给送个优惠券,给设置些什么什么权限的,注册成功后可能要做很多事情。这时候观察者模式就有用了,那么重构如下:
首先定义观察者接口,并且定义观察到之后的行为
//Observer是观察员的意思 监视者Register这个行为 public interface RegObserver { //handle:处理处理注册成功这个事 void handleRegSuccess(Long userId); }
实现观察者行为
public class RegGiveShitObserverImpl implements RegObserver { private ShitService shitService; @Override public void handleRegSuccess(Long userId) { //里面实现了送给某人一坨屎 shitService.sendShit(userId); } }
public class RegWelcomObserverImpl implements RegObserver { private SendMessageService sendMessageService; @Override public void handleRegSuccess(Long userId) { //里面实现了欢迎某某人的到来 sendMessageService.sendMessage(userId); } }
当行为发生的时候,通知观察者
public class UserService { private List<RegObserver> regObservers = new ArrayList<>(); //将观察者需要执行的行为于执行被观察者前设置 public void setRegObservers(List<RegObserver> observers) { regObservers.addAll(observers); } public Long register(String telephone, String password) { //注册行为 ...
//通知观察者 for (RegObserver observer : regObservers) { observer.handleRegSuccess(userId); } return userId; } }
当有新的观察者行为出现时,只需要定义新的观察者实现类并实现行为,在使用前通过 setRegObservers() 加入进行为列表即可,注册用户类是不会进行修改的,对修改关闭,对新增开放。
Google Guava EventBus
我们知道,框架的作用:隐藏实现细节,降低开发难度,做到代码复用,解耦业务与非业务代码,让程序员聚焦业务开发。
Google Guava EventBus 就是一个比较著名的 EventBus 框架,它不仅仅支持异步非阻塞模式,同时也支持同步阻塞模式。
那么以上的代码如何通过Google Guava EventBus实现呢,我们接着往下看:
两个监听实现类的修改:
public class RegWelcomeObserverImpl implements RegObserver { private SendMessageService sendMessageService; @Override @Subscribe public void handleRegSuccess(Long userId) { //里面实现了欢迎某某人的到来 sendMessageService.sendMessage(userId); } }
public class RegGiveShitObserverImpl implements RegObserver { private ShitService shitService; @Override @Subscribe public void handleRegSuccess(Long userId) { //里面实现了送给某人一坨屎 shitService.sendShit(userId); } }
被监听类(注册用户的修改)
public class UserService { private EventBus eventBus; private static final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20; public UserService() { //这么写就是同步阻塞模式 eventBus = new EventBus(); //这么写就是异步非阻塞模式 eventBus = new AsyncEventBus(Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE)); } public void setRegObservers(List<RegObserver> observers) {
//注册监听事件 observers.forEach(observer -> eventBus.register(observer)); } public Long register(String telephone, String password) { //注册行为 ... Long userId = 123456L; //发布一个事件 eventBus.post(userId); return userId; } }
当然你也可以使用完全不同的两个类去做实现,比如:
public class ObserverA{
@Subscribe
public void doA(A a) {
//do something
...
}
}
public class ObserverB{
@Subscribe
public void doB(B b) {
//do something
...
}
@Subscribe
public void doC(C c) { //do something ... }
}
在调用时,根据类型触发指定的行为:
public class test{ public void doSometing(){ private ObserverA observerA = new ObserverA(); private ObserverB observerB = new ObserverB(); EventBus eventBus = new EventBus(); //注册观察者 eventBus.register(observerA); eventBus.register(observerB); //发布事件 eventBus.post(new A()); eventBus.post(new B());
eventBus.post(new C());
} }
EventBus中有两个主要函数register()、post()的实现原理图如下:
从图中我们可以看出,最关键的一个数据结构是 Observer 注册表,记录了消息类型和可接收消息函数的对应关系。
当调用 register() 函数注册观察者的时候,EventBus 通过解析 @Subscribe 注解,生成 Observer 注册表。
当调用 post() 函数发送消息的时候,EventBus 通过注册表找到相应的可接收消息的函数,然后通过 Java 的反射语法来动态地创建对象、执行函数。
对于同步阻塞模式,EventBus 在一个线程内依次执行相应的函数。
对于异步非阻塞模式,EventBus 通过一个线程池来执行相应的函数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-04-08 windows 查询端口占用并停用