发布-订阅者模式和事件监听器模式

作者: _挑灯看剑_ | 来源:发表于2016-11-04 17:04 被阅读3196次
  • 订阅者模式


订阅者和发布者模式,通常用于消息队列中。一般有两种形式来实现消息队列,一是使用生产者和消费者来实现,二是使用订阅-发布者来实现。其中使用订阅者和发布者实现消息队列的方式,就会用订阅者模式。

所谓的订阅者,就像我们在日常生活中,订阅报纸一样。我们订阅报纸的时候,通常都得需要在报社或者一些中介机构进行注册。当有新版的报纸发刊的时候,邮递员就需要向订阅该报纸的人,依次发放报纸。因此代码实现该模式,通常需要两个步骤:

1、初始化发布者、订阅者。
2、订阅者需要注册到发布者,发布者发布消息时,依次向订阅者发布消息。

  • 订阅者注册
订阅者注册.png
  • 发布者发布消息
发布者发布消息.png

代码实现如下(一个常见的例子,双十一商品打折,只有关注该商品的客户才能收到该商品打折的信息):
商品信息类主要有成员变量来存放所有关注该商品的顾客(ArrayList),同时存在发布消息的方法,客户关注商品的方法(即向客户容器中添加客户)等。

//商品类
class Product{
    private ArrayList<Client> clientArrayList = new ArrayList<Client>();

    public void notify(String info){
        //对于所有关注该商品的顾客进行遍历,依次将信息发布出去
        for(Client client:clientArrayList){
            client.receiveInfo(info);
        }
    }

    public void register(Client client){
        clientArrayList.add(client);
    }
}

客户类,即订阅者,用来接受商品(发布者)发布的打折信息:

class ConcreteClient {

    private String name;

    public ConcreteClient(String name){
        this.name = name;
    }
    //用来接受发布者发布的消息
    public void receiveInfo(String info) {
        System.out.println(this.name + ":收到信息(" + info + ")");
    }
}

测试:

@Test
public void test(){
        Product product = new Product();

        //a 用户关注商品
        product.register(new ConcreteClient("a"));
        //b 用户关注商品
        product.register(new ConcreteClient("b"));
        //c 用户关注商品
        product.register(new ConcreteClient("c"));
        //d 用户关注商品
        product.register(new ConcreteClient("d"));

        product.notify("商品编号为D1403121717大减价");
    }

运行结果:

a:收到信息(商品编号为D1403121717大减价)
b:收到信息(商品编号为D1403121717大减价)
c:收到信息(商品编号为D1403121717大减价)
d:收到信息(商品编号为D1403121717大减价)
  • 事件监听器模式


概念

事件监听器模式重要的三个概念:事件+事件监听器+事件源

  • 在javascript中,这样的模式很常见,例如:
    $("#div").addEventListener("click",function(event){});
    事件源:$("#div") 即id = div的html元素
    事件:click事件对应的对象
    事件监听器:function(event){ } 匿名函数
  • 在java awt编程中:
Button button = new Button();
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent actionEvent){
      //当触发事件时,执行代码
}
});

事件源:Button组件
事件:click事件对应的对象 ActionEvent
事件监听器:ActionListener接口中的actionPerformed方法

事件监听的过程如下:

1、事件源通常会有很多事件类型,比如点击类型,加载类型,关闭类型等等。此时,事件源就会添加这些个事件对应的事件监听器。
2、事件监听器的作用就是:当事件源的某个事件被触发时,就会调用这个事件对应的事件监听器的处理事件的方法。

事件处理过程.png

因此使用代码实现事件监听器模式,需要以下过程:

  • 初始化事件源类,事件监听器所在的类(实现接口EventListener)事件类(继承EventObject类)
  • 对于每一个事件,都分为更为具体的事件(如点击事件,分为双击和单击。加载事件,分为开始加载,成功加载,加载异常,关闭页面等事件)。点击事件对应ClickEvent,其种类可以分为DoubleClick和SingleClick事件(通常使用ClickEvent类的类常量表示)。而ClickEvent事件则对应ClickEventListener事件监听器,更为具体的DoubleClick事件对应ClickEventListener事件的doubleClick方法,而SingleClick事件对应ClickEventListener事件的singleClick方法。
  • 确认要触发的事件,实例化事件类,ClickEvent,并且赋值其成员变量,是赋值其为DoubleClick还是SingleClick。
  • 当事件源触发click事件的时候,通过条件判断(if-else/switch-case),则会使用ClickEventListener事件监听器。接下来继续判断click事件的类型和种类,如果是DoubleClick事件,则调用ClickEventListener类的doubleClick方法。如果是SingleClick事件,则调用ClickEventListener类的singleClick方法。

上述处理过程可以用下图表示出来:

编码过程.png

具体代码实现过程:
定义三个事件:MouseMovationEvent事件,LoadPageEvent事件,ClickEvent事件

/**
 *页面加载事件
 */
class LoadPageEvent extends EventObject{

    private int id; //表示具体事件的类型

    public static final int PAGELOAD_SUCCESS = 1;  //页面加载成功事件
    public static final int PAGELOAD_ERROR = 2; //页面加载失败事件
    public static final int PAGELOAD_START = 3; //页面开始加载事件
    public static final int PAGELOAD_CLOSE = 4; //页面关闭事件

    //建立LoadPageEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型
    public LoadPageEvent(Object source,int id){
        super(source);
        switch (id){
            case PAGELOAD_CLOSE:this.id = PAGELOAD_CLOSE;break;
            case PAGELOAD_ERROR:this.id = PAGELOAD_ERROR;break;
            case PAGELOAD_START:this.id = PAGELOAD_START;break;
            case PAGELOAD_SUCCESS:this.id = PAGELOAD_SUCCESS;break;
        }
    }

    public LoadPageEvent(Object source){
        super(source);
    }
    
    //获取事件的具体的事件类型
    public int getId(){
        return this.id;
    }
}

/**
 * 点击事件
 */
class ClickEvent extends EventObject{
    
    //具体的事件类型
    private int id;

    public final static int SINGLE_CLICK = 1; //单击事件
    public final static int DOUBLE_CLICK = 2; //双击事件


    public ClickEvent(Object source){
        super(source);
    }

    //建立ClickEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型
    public ClickEvent(Object source,int id){
        super(source);
        switch(id){
            case SINGLE_CLICK:this.id = SINGLE_CLICK;break;
            case DOUBLE_CLICK:this.id = DOUBLE_CLICK;break;
        }
    }

    public int getId(){
        return this.id;
    }

}

/**
 * 鼠标移动事件
 */

class MouseMovationEvent extends EventObject{
    //具体的事件类型
    private int id;

    public static final int MOVE_LEFT = 0; //鼠标左移事件
    public static final int MOVE_RIGHT = 1; //鼠标右移事件

    //建立MouseMovationEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型
    public MouseMovationEvent(Object source,int id){
        super(source);
        switch(id){
            case MOVE_LEFT:this.id = MOVE_LEFT;break;
            case MOVE_RIGHT:this.id = MOVE_RIGHT;break;
        }
    }

    public MouseMovationEvent(Object source){
        super(source);
    }

    public int getId(){
        return this.id;
    }
}

定义三个事件监听器:MouseMovationEventListener监听器,LoadPageEventListener事件监听器,ClickEventListener事件监听器

////////////////////页面加载事件监听器//////////////////
class LoadPageListener implements EventListener{

    //LoadPageEvent的成功加载事件触发时,则执行该方法
    public void onSuccess(LoadPageEvent e){
        System.out.println("页面加载成功");
    }

    //LoadPageEvent的开始加载事件触发时,则执行该方法
    public void onStart(LoadPageEvent e){
        System.out.println("页面开始加载");
    }

    //LoadPageEvent的关闭事件触发时,则执行该方法
    public void onClose(LoadPageEvent e){
        System.out.println("页面关闭");
    }

    //LoadPageEvent的异常加载事件触发时,则执行该方法
    public void onError(LoadPageEvent e){
        System.out.println("页面加载出错");
    }
}

///////////页面点击监听器///////////////////
class ClickListener implements EventListener{

    //ClickEvent的单击事件触发时,则执行该事件
    public void onSingle(ClickEvent clickEvent){
        System.out.println("鼠标单击");
    }
    
     //ClickEvent的双击事件触发时,则执行该事件
    public void onDouble(ClickEvent clickEvent){
        System.out.println("鼠标双击");
    }
}

///////////鼠标移动监听器///////////////////
class MouseMovationListener implements EventListener{
    
    //MouseMovationEvent的左移事件触发时,则执行该方法
    public void onLeft(MouseMovationEvent mouseMovationEvent){
        System.out.println("鼠标左划");
    }
    
    //MouseMovationEvent的右移事件触发时,则执行该方法
    public void onRight(MouseMovationEvent mouseMovationEvent){
        System.out.println("鼠标右划");
    }
}

定义一个事件源:Page页面。事件源至少有两个方法,一个是为自身添加事件监听器,一个是触发事件方法。当触发事件时,会根据传入的事件获取事件类型。从而确定应该实例化哪一个事件监听器类,并且调用事件监听器的哪一个方法。

///////////////事件源////////////
class Page{

    private LoadPageListener loadPageListener;
    private ClickListener clickListener;
    private MouseMovationListener mouseMovationListener;

    //为自身注册事件监听器
    public void addLoadPageListener(LoadPageListener loadPageListener){
        this.loadPageListener = loadPageListener;
    }

    public void addClickListener(ClickListener clickListener){
        this.clickListener = clickListener;
    }

    public void addMouseMovationListener(MouseMovationListener mouseMovationListener){
        this.mouseMovationListener = mouseMovationListener;
    }
    
    //触发事件
    public void trigger(EventObject e){

        //判断事件类型,确定使用的事件监听器类,并且确定调用事件监听器类的哪个方法。
        if(e instanceof LoadPageEvent){
            LoadPageEvent le = (LoadPageEvent)e;
            switch(le.getId()){
                case LoadPageEvent.PAGELOAD_CLOSE:this.loadPageListener.onClose(le);break;
                case LoadPageEvent.PAGELOAD_ERROR:this.loadPageListener.onError(le);break;
                case LoadPageEvent.PAGELOAD_START:this.loadPageListener.onStart(le);break;
                case LoadPageEvent.PAGELOAD_SUCCESS:this.loadPageListener.onSuccess(le);break;
            }
        }
        else if(e instanceof ClickEvent){
            ClickEvent ce = (ClickEvent)e;
            switch(ce.getId()){
                case ClickEvent.DOUBLE_CLICK:this.clickListener.onDouble(ce);break;
                case ClickEvent.SINGLE_CLICK:this.clickListener.onSingle(ce);break;
            }
        }
        else if(e instanceof  MouseMovationEvent){
            MouseMovationEvent me = (MouseMovationEvent)e;
            switch(me.getId()){
                case MouseMovationEvent.MOVE_LEFT:this.mouseMovationListener.onLeft(me);break;
                case MouseMovationEvent.MOVE_RIGHT:this.mouseMovationListener.onRight(me);break;
            }
        }
    }
}

以上代码均在Intellij Idea环境下测试通过。

相关文章

  • 异步编程解决方案

    事件发布/订阅模式 事件监听器模式是一种广泛用于异步编程的模式,是回调函数的事件化,又称发布/订阅模式。 事件发布...

  • 观察者模式和发布订阅模式的区别

    观察者模式和发布订阅模式最大的区别就是发布订阅模式有个事件调度中心。 在观察者模式中,观察者需要直接订阅目标事件;...

  • 发布-订阅者模式和事件监听器模式

    订阅者模式 订阅者和发布者模式,通常用于消息队列中。一般有两种形式来实现消息队列,一是使用生产者和消费者来实现,二...

  • 发布-订阅者模式和事件监听器模式

    订阅者模式 订阅者和发布者模式,通常用于消息队列中。一般有两种形式来实现消息队列,一是使用生产者和消费者来实现,二...

  • 订阅发布模式

    Q:何为发布订阅模式?A:这是一种发布者和订阅者之间由事件驱动产生关系的软件开发模式Q:发布订阅模式哪里可以看到?...

  • SpringBoot源码分析之监听器

    1. spring监听器的设计 主要是基于发布订阅者设计模式,由广播器(MultiCaster)、事件(Event...

  • 设计模式之观察者模式

    观察者模式(常用设计模式) 观察者模式,是类的行为模式,又叫做发布订阅模式,模型视图模式,源监听器模式。 动机 将...

  • 简单易懂设计模式 之 观察者(observer)

    观察者模式是对象的行为模式,又叫发布-订阅模式,模型-视图模式,源-监听器模式或从属者模式;观察者模式定义了一种一...

  • 如何理解js的发布-订阅模式

    发布-订阅模式/观察者模式 发布-订阅模式也叫观察者模式,这是一个一对多的关系,可以多个订阅者订阅同一个事件,当事...

  • 传值-跨层传值和无嵌套关系

    1、订阅者和发布者模式 EventEmmiter 2、componnentdidmount组件挂在后后:订阅事件;...

网友评论

  • 258b9af7165d:订阅者和发布者看过很多文章,一直没理解,你这个简单易懂

本文标题:发布-订阅者模式和事件监听器模式

本文链接:https://www.haomeiwen.com/subject/avkwuttx.html