前言:设计模式,一个老掉牙的话题,一个永恒的话题。所有的Java开发,都必须经过的话题,都必须学习的话题。但是其实很多时候自己只是看了,了解了,然后忘了。所以还是想从头梳理一遍重要的设计模式,给自己看。不求逻辑多清晰,内容多完善,UML图画的多严谨,理解、会意就好。
遇到什么问题
在介绍观察者模式的最后(查看设计模式私人笔记-观察者模式),我们最后提到了观察者模式的局限性,并引入了两个新的模式,事件监听模式和发布订阅模式。
首先说明一点,我查看了不少资料,事件监听器模式和发布订阅模式都不属于经典的GOF模式。在我看来,这两种模式都是观察者模式的延展。 下面就对这事件监听模式做一个简单介绍,并和观察者模式进行一个对比。
事件监听模式
事件监听模式,广泛的存在GUI,Servet,Spring等等地方。举一个简单的例子:
在Servet中,为我们提供了ServletContextListener接口,用于监听ServletContext上下文的创建事件。一旦Servlet上下文创建成功,Servlet容器就会对外抛出一个事件对象,告知所有注册在Servlet容器中的用于监听ServletContext相关事件的监听器。
![449f3431ec1dd5aa74060e90eadcf7ba.png](https://img-blog.csdnimg.cn/img_convert/449f3431ec1dd5aa74060e90eadcf7ba.png)
例如上面代码,是经典的Spring容器加载的监听器,就是实现了ServletContextListener,要让监听器起作用,只需要配置在web.xml中即可:
![be25e1dba28d06b95557729e7c6727f8.png](https://img-blog.csdnimg.cn/img_convert/be25e1dba28d06b95557729e7c6727f8.png)
这一步就相当于在注册监听器。
Java中的事件监听模式
那么事件监听模式到底是怎么样的呢?在java.util包中,其实已经为我们做好了事件监听模式的相关一些支持。
![85287b4efdbdf0b6350ede07b89994d7.png](https://img-blog.csdnimg.cn/img_convert/85287b4efdbdf0b6350ede07b89994d7.png)
这是第一个接口,用于标记事件监听器,类似观察者模式中的Observer接口,只是其中并没有定义任何方法,这仅仅只是一个标记接口。
![90da4615221134179928298850064cea.png](https://img-blog.csdnimg.cn/img_convert/90da4615221134179928298850064cea.png)
这个类定义了一个事件对象,这个事件对象用于在被监听者和监听器之间传递数据。我们可以这样理解EventObject:
![a6f8aeb6469596331363943f13c6b61c.png](https://img-blog.csdnimg.cn/img_convert/a6f8aeb6469596331363943f13c6b61c.png)
大家应该还记得这个观察者模式的类图,在观察者模式中,当主题状态变化的时候,由Subject调用notifyChange方法,在该方法中,依次调用Observer的update方法,而update方法中的第一个参数,Subject就是主题对象,也就可以理解为这里面的事件对象的事件源。意思就是之前是直接调用方法,而这里变成了传递一个事件对象。
我们可以来看看JAVA中自己对事件监听的使用模式。我们使用AWT中的例子来说明(没有用过也无所谓,看懂代码就行)。
![4fbedb21c2b43f9249ee1e72f1f68421.png](https://img-blog.csdnimg.cn/img_convert/4fbedb21c2b43f9249ee1e72f1f68421.png)
首先来看awt中事件监听器的基础接口,ActionListener,在这里定义了actionPerformed方法,这个方法就是监听器监听到某个事件之后要做的逻辑。该方法传入一个ActionEvent对象,我们来看看这个事件对象的代码:
![bff2132dab9e3a4b950545aef0989ff1.png](https://img-blog.csdnimg.cn/img_convert/bff2132dab9e3a4b950545aef0989ff1.png)
首先我们看到的是AWTEvent类,这个类是ActionEvent的基类。可以看到这个类继承了EventObject,并且在这个类中包含了一些自己的属性(状态),比如id(简单理解为事件序列号),consumed(是否被处理)等;关键的,在构造方法中,对事件对象本身拥有的属性做了初始化操作。
![24e93b48995592e56e53e35960b2217b.png](https://img-blog.csdnimg.cn/img_convert/24e93b48995592e56e53e35960b2217b.png)
可以看到,ActionEvent其实和AWTEvent结构是一模一样的,只是它又多了几个这个事件相关的属性。并且同样也在构造器中进行了初始化操作。
事件相关的基础类看完了,我们来看下事件源,就是我们的主题对象,在swing中,有一个基础的类:组件(JComponent)定义了基础的组件,比如按钮,等