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

订阅者模式

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

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

1、初始化发布者、订阅者。

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

订阅者注册


发布者发布消息


代码实现如下(一个常见的例子,双十一商品打折,只有关注该商品的客户才能收到该商品打折的信息):

商品信息类主要有成员变量来存放所有关注该商品的顾客(ArrayList),同时存在发布消息的方法,客户关注商品的方法(即向客户容器中添加客户)等。

//商品类

classProduct{

       private ArrayList clientArrayList =newArrayList();

       public voidnotify(String info){

              //对于所有关注该商品的顾客进行遍历,依次将信息发布出去

             for(Client client:clientArrayList){

                    client.receiveInfo(info);

               }

     }

    publicvoidregister(Client client){

          clientArrayList.add(client);

     }

}

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

class ConcreteClient{

      privateString name;

      publicConcreteClient(String name){

             this.name = name;

       }

      //用来接受发布者发布的消息

       public void receiveInfo(String info){

             System.out.println(this.name +":收到信息("+ info +")");

       }

}

测试:

@Test

public void test(){

      Product product =newProduct();//a 用户关注商品                                                              product.register(newConcreteClient("a"));//b 用户关注商品                                                product.register(newConcreteClient("b"));//c 用户关注商品                                                product.register(newConcreteClient("c"));//d 用户关注商品                                                product.register(newConcreteClient("d"));                                                                          product.notify("商品编号为D1403121717大减价");

}

运行结果:

a:收到信息(商品编号为D1403121717大减价)

b:收到信息(商品编号为D1403121717大减价)

c:收到信息(商品编号为D1403121717大减价)

d:收到信息(商品编号为D1403121717大减价)

事件监听器模式

概念

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

1、在javascript中,这样的模式很常见,例如:

$("#div").addEventListener("click",function(event){});

事件源:$("#div") 即id = div的html元素

事件:click事件对应的对象

事件监听器:function(event){ } 匿名函数

2、在java awt编程中:

Button button =newButton();

button.addActionListener(newActionListener(){

       publicvoidactionPerformed(ActionEvent actionEvent){

           //当触发事件时,执行代码

        }

});

事件源:Button组件

事件:click事件对应的对象 ActionEvent

事件监听器:ActionListener接口中的actionPerformed方法

事件监听的过程如下:

1、事件源通常会有很多事件类型,比如点击类型,加载类型,关闭类型等等。此时,事件源就会添加这些个事件对应的事件监听器。

2、事件监听器的作用就是:当事件源的某个事件被触发时,就会调用这个事件对应的事件监听器的处理事件的方法。


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

初始化事件源类,事件监听器所在的类(实现接口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方法。

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

具体代码实现过程:

定义三个事件:MouseMovationEvent事件,LoadPageEvent事件,ClickEvent事件

/***页面加载事件*/

class LoadPageEventex tendsEventObject{

       privateintid;//表示具体事件的类型

       publicstaticfinalintPAGELOAD_SUCCESS =1;//页面加载成功事件                                     publicstaticfinalintPAGELOAD_ERROR =2;//页面加载失败事件                                         publicstaticfinalintPAGELOAD_START =3;//页面开始加载事件                                           publicstaticfinalintPAGELOAD_CLOSE =4;//页面关闭事件

      //建立LoadPageEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型

     public LoadPageEvent(Object source,intid){

            super(source);

            switch(id){

                 casePAGELOAD_CLOSE:this.id = PAGELOAD_CLOSE;break;

                casePAGELOAD_ERROR:this.id = PAGELOAD_ERROR;break;

                casePAGELOAD_START:this.id = PAGELOAD_START;break; 

                casePAGELOAD_SUCCESS:this.id = PAGELOAD_SUCCESS;break;

          }   

    }

    public LoadPageEvent(Object source){

         super(source);   

    }//获取事件的具体的事件类型

    publicintgetId(){

        returnthis.id;

    }

}/**

* 点击事件

*/class

ClickEvent extends EventObject{

        //具体的事件类型

        privateintid;

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

        publicfinalstaticintDOUBLE_CLICK =2;//双击事件

        public ClickEvent(Object source){

              super(source);   

      }

//建立ClickEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型public ClickEvent(Object source,intid){

        super(source);

       switch(id){

           caseSINGLE_CLICK:this.id = SINGLE_CLICK;break;

           caseDOUBLE_CLICK:this.id = DOUBLE_CLICK;break;

        }   

}

publicintgetId(){returnthis.id; 

  }}/**

* 鼠标移动事件

*/class MouseMovationEvent extends EventObject{

//具体的事件类型privateintid;publicstaticfinalintMOVE_LEFT =0;//鼠标左移事件

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

//建立MouseMovationEvent实例对象,要指明具体的id类型,构造函数根据id来指定具体的事件类型

public MouseMovationEvent(Object source,intid){

           super(source);switch(id){

                 caseMOVE_LEFT:this.id = MOVE_LEFT;break;

                 caseMOVE_RIGHT:this.id = MOVE_RIGHT;break;

        }   

}

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

    }

publicintgetId(){

returnthis.id;

    }

}

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

////////////////////页面加载事件监听器//////////////////classLoadPageListenerimplementsEventListener{//LoadPageEvent的成功加载事件触发时,则执行该方法publicvoidonSuccess(LoadPageEvent e){ System.out.println("页面加载成功"); }//LoadPageEvent的开始加载事件触发时,则执行该方法publicvoidonStart(LoadPageEvent e){ System.out.println("页面开始加载"); }//LoadPageEvent的关闭事件触发时,则执行该方法publicvoidonClose(LoadPageEvent e){ System.out.println("页面关闭"); }//LoadPageEvent的异常加载事件触发时,则执行该方法publicvoidonError(LoadPageEvent e){ System.out.println("页面加载出错"); }}///////////页面点击监听器///////////////////classClickListenerimplementsEventListener{//ClickEvent的单击事件触发时,则执行该事件publicvoidonSingle(ClickEvent clickEvent){ System.out.println("鼠标单击"); }//ClickEvent的双击事件触发时,则执行该事件publicvoidonDouble(ClickEvent clickEvent){ System.out.println("鼠标双击"); }}///////////鼠标移动监听器///////////////////classMouseMovationListenerimplementsEventListener{//MouseMovationEvent的左移事件触发时,则执行该方法publicvoidonLeft(MouseMovationEvent mouseMovationEvent){ System.out.println("鼠标左划"); }//MouseMovationEvent的右移事件触发时,则执行该方法publicvoidonRight(MouseMovationEvent mouseMovationEvent){ System.out.println("鼠标右划"); }}

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

///////////////事件源////////////classPage{privateLoadPageListener loadPageListener;privateClickListener clickListener;privateMouseMovationListener mouseMovationListener;//为自身注册事件监听器publicvoidaddLoadPageListener(LoadPageListener loadPageListener){this.loadPageListener = loadPageListener; }publicvoidaddClickListener(ClickListener clickListener){this.clickListener = clickListener; }publicvoidaddMouseMovationListener(MouseMovationListener mouseMovationListener){this.mouseMovationListener = mouseMovationListener; }//触发事件publicvoidtrigger(EventObject e){//判断事件类型,确定使用的事件监听器类,并且确定调用事件监听器类的哪个方法。if(einstanceofLoadPageEvent){ LoadPageEvent le = (LoadPageEvent)e;switch(le.getId()){caseLoadPageEvent.PAGELOAD_CLOSE:this.loadPageListener.onClose(le);break;caseLoadPageEvent.PAGELOAD_ERROR:this.loadPageListener.onError(le);break;caseLoadPageEvent.PAGELOAD_START:this.loadPageListener.onStart(le);break;caseLoadPageEvent.PAGELOAD_SUCCESS:this.loadPageListener.onSuccess(le);break; } }elseif(einstanceofClickEvent){ ClickEvent ce = (ClickEvent)e;switch(ce.getId()){caseClickEvent.DOUBLE_CLICK:this.clickListener.onDouble(ce);break;caseClickEvent.SINGLE_CLICK:this.clickListener.onSingle(ce);break; } }elseif(einstanceofMouseMovationEvent){ MouseMovationEvent me = (MouseMovationEvent)e;switch(me.getId()){caseMouseMovationEvent.MOVE_LEFT:this.mouseMovationListener.onLeft(me);break;caseMouseMovationEvent.MOVE_RIGHT:this.mouseMovationListener.onRight(me);break; } } }}

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


來源:简书  https://www.jianshu.com/p/40986c26315a

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 订阅者模式 订阅者和发布者模式,通常用于消息队列中。一般有两种形式来实现消息队列,一是使用生产者和消费者来实现,二...
    _挑灯看剑_阅读 13,735评论 1 18
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,467评论 1 11
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,039评论 0 3
  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,046评论 1 10
  • 22、JQ的基础语法、核心原理和项目实战 jQ的版本和下载 jQuery版本 1.x:兼容IE6-8,是目前PC端...
    萌妹撒阅读 1,740评论 0 0