Java设计模式——观察者模式(事件监听)

摘要:
最近,我查看了Tomcat和Spring的源代码,在启动时注册了各种侦听器,并在事件触发时执行。这里,使用设计模式中的观察者模式。如果我们忘记了设计模式,我们应该怎么做?让我们看看Java中的事件处理机制是如何实现的!在handleEvent中,我们可以通过事件对象获得所需的东西。这里有一个最基本的源,即switch对象。2.事件和事件源

最近在看Tomcat和Spring的源码,在启动的时候注册了各种Listener,事件触发的时候就执行,这里就用到了设计模式中的观察者模式。

引-GUI中的事件监听

想想以前在学Java的GUI编程的时候,就用到了事件的注册监听,然后写了一个小程序试验一下:
点击按钮触发相应的事件

public class ButtonTest extends JFrame {
    ButtonTest() {
        JPanel panel = new JPanel();
        JButton button1 = new JButton("按钮一");
        JButton button2 = new JButton("按钮二");

        panel.add(button1);
        panel.add(button2);
        this.getContentPane().add(panel);
        this.setVisible(true);

        button1.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("你按了按钮一");
                    }
                });
        button2.addActionListener(
                new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        System.out.println("你按了按钮二");
                    }
                });
    }

    public static void main(String args[]) {
        new ButtonTest();
    }
}

嗯,写起来确实很简单,这是封装好的,我们只需要注册事件,并实现对应的事件响应就行了。
那么这种神奇的模式是怎么实现的呢?
以下为我的分析过程。o(╯□╰)o
首先,我们来回归本质,看看到底这种模式方便在哪儿。如果不这么做的话,我们需要再每次点击按钮的代码里面把响应的内容加上,如果有一千个事件响应,我们需要把这一千个响应都写到点击按钮这个方法里面,Oh!!MyGod!太麻烦了!
如果忘掉设计模式,我们想想这种事情应该怎么做呢?
我们需要把这一千个响应管理起来,然后在事件触发的时候,把这些响应统一调用,不就行了?问题来了...
1.如何管理起来?
2.每个响应都不一样,每次写响应的时候都需要去点击事件那里加一段,改动以前的代码,此乃编程之大忌,耦合度太高
想想回答,
1.把每一个响应都通过一个响应链来管理,触发事件时,遍历这个响应链,做出响应。
2.可以定义一个接口,在响应的地方传入该接口,调用接口的方法。这样,每次需要响应的时候,实现该接口,传入具体响应的对象,这样我们就只用关心如何响应了,不用关心如何去调用响应的办法。
晕了晕了晕了……
来看看Java中的事件处理机制是如何实现的吧!

Java中的事件处理机制

来自维基百科的结构图:
Java设计模式——观察者模式(事件监听)第1张
图看看就行。
场景:按下开关,灯做出的响应。
1.定义事件对象

/**
 * 事件对象。继承EventObject
 * Created by HuangYQ on 2016/5/31.
 */
public class SwitchEvent extends EventObject {

    private String switchState;     //表示开关的状态

    public SwitchEvent(Switch source, String switchState) {
        super(source);
        this.switchState = switchState;
    }

    public void setSwitchState(String switchState) {
        this.switchState = switchState;
    }

    public String getSwitchState() {
        return switchState;
    }
}

2.定义事件监听接口

/**
 * 事件监听接口
 * Created by HuangYQ on 2016/5/31.
 */
public interface SwitchListener extends EventListener {

    public void handleEvent(SwitchEvent switchEvent);

}

我们可以在这里,再定义事件监听类,这些类具体实现了监听功能和事件处理功能。也可以用下面主程序中的匿名内部类来实现。原理一样。

3.定义事件源对象(具体的事件源,比如说,你点击一个button,那么button就是event source,要想使button对某些事件进行响应,你就需要注册特定的listener。这里指开关)

/**
 * 电源开关
 * 事件源对象,类似于Swing中的button
 * Created by HuangYQ on 2016/5/31.
 */
public class Switch {

    private Vector switchListenerList = new Vector();

    public void addListener(Object listener) {
        switchListenerList.add(listener);
    }

    protected void open() {
        SwitchEvent switchEvent = new SwitchEvent(this, "开");
        notifyListeners(switchEvent);
    }

    protected void close() {
        SwitchEvent switchEvent = new SwitchEvent(this, "关");
        notifyListeners(switchEvent);
    }

    private void notifyListeners(SwitchEvent switchEvent) {
        Iterator iterator = switchListenerList.iterator();
        while (iterator.hasNext()) {
            SwitchListener switchListener = (SwitchListener) iterator.next();
            switchListener.handleEvent(switchEvent);
        }
    }

}

4.主程序

public class SwitchMain {

    public static void main(String[] args) {
        Switch switchTest = new Switch();
        switchTest.addListener(new SwitchListener() {
            @Override
            public void handleEvent(SwitchEvent switchEvent) {
                //Do what ever you want !
                System.out.println(switchEvent.getSwitchState());
            }
        });

        //触发
        switchTest.open();
        switchTest.close();
    }
}

运行:

开
关

仔细分析下程序,和我们之前YY的监听器其实大致一样,
个人觉得最难理解的就是事件对象SwitchEvent了。
问题
1.为什么要继承EventObject?不继承行不行
2.事件和事件源有什么联系,为什么还需要事件对象
答:
1.继承自EventObject只是为了规范,不实现当然可以。在handleEvent的时候,我们可以通过事件对象拿到我们需要的东西,这里面就有最基本的source,也就是这里的switch对象了,
2.事件和事件源关系并不大,事件对象里面包含source罢了,通过构造函数注入。

[参考]

设计模式学习----观察者模式(事件监听实现)
java事件处理机制(自定义事件)

免责声明:文章转载自《Java设计模式——观察者模式(事件监听)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇查看Linux声卡基本信息[转载]【fcntl系统调用】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

python- generator生成器

什么是生成器? 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的l...

如何正确使用Java泛型

前言 Java 1.5之前是没有泛型的,以前从集合中读取每个对象都必须先进行转换,如果不小心存入集合中对象类型是错的,运行过程中转换处理会报错。有了泛型之后编译器会自动帮助转换,使程序更加安全,但是要正确使用泛型才能取得事半功倍的效果。 本文主要从不要使用原生类型,泛型方法,限制通配符,类型安全的异构容器四个部分来说明如何正确使用Java泛型。主要参考资...

RxJava入门

项目小版本上线,抽空简单学习了下久仰大名的RxJava 一、引入 个人觉得rxjava的特点: 强大灵活的事件流处理(多线程/多事件/复合对象) 强大灵活优雅简洁的异步 链式调用 可自动Lambda化   实现:RxJava 是通过一种扩展的观察者模式来实现的 类比 类比 实际 实际 职责 演讲者 Button (可)被订阅者 (同右)...

遍历QMap引发异常处理

  引言 用常规方法遍历QMap,删除满足条件元素时出现“读取位置0xXXX时发生访问冲突”。查看“调用堆栈”指向QMap<int,int>::iterator::operator++()和QMapNode<int,int>::nextNode() 定位为删除iterator中元素引起iterator的遍历异常,特记录如下: 常规错...

02_编程规约——集合处理

1.【强制】关于hashCode和equals的处理,必须遵循如下规则 1.1 只要重写equals,就必须重写hashCode。 1.2 因为Set存储的是不重复对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。 1.3 如果自定义对象为Map的键,那么必须重写hashCode和equals。 说明:String重...

android开发(7) 文件下载

我们在开发中经常需要从服务器下载文件,下载的内容可能有交换的信息,缓存的图片,程序更新包等。我们使用URLConnection来实现下载。先看几行代码: String urlDownload = "";urlDownload = "http://www.baidu.com/img/baidu_sylogo1.gif"; URL url = new URL...