事件的订阅发布机制以及在前端中的应用

引用自https://en.wikipedia.org/wiki/Publish–subscribe_pattern

简介

在软件结构中,发布订阅是一种消息模式(一种面向网络结构的模式,描述了两种不同部分的消息怎么去互相连接和沟通),其中包括非直接传递信息的publisher叫publish,和接收信息的subscriber叫subscribe。subscriber把不同的消息进行分类,同时不需要知道subscriber是谁。subscriber订阅感兴趣的分类,并只接收订阅的分类中信息,同时也不需要知道publisher是谁。
发布订阅机制通常和消息队列机制一样,也是一种典型的大型的面相信息中间件系统的一部分。

消息过滤

在订阅发布模型中,subscriber只接收所有发布的信息中的一部分。选择接收的信息的过程叫过滤。有两种过滤机制:
基于主题:
publisher发布不同主题的消息,subscriber接收选择已订阅的主题的消息。不同subscriber订阅相同主题的话接收的信息相同。publisher定义不同subscriber可以订阅的信息的类别。
基于内容
只有当发送的消息的属性或者内容符合subscriber制定的规则,消息才会发送给subscribes。

一些系统支持两种过滤机制的混合使用,即publisher发布一个消息到一个主题,同时subscriber制定了基于内容的限制的订阅在这个主题上。

应用

在很多订阅发布系统中,publisher发布信息到一个信息中介或者是事件公交车,同时subscriber注册订阅信息到这个信息中介,同时信息中介担当着过滤的作用。信息中介一般扮演着暂存和继续发送从publisher按照路线发送到subscriber的消息的角色。另外,信息中介可能会在按路线运送消息前在消息队列中对消息的优先级进行处理。

subscriber可能会注册特定的消息在构建时,初始化时或者运行时。GUI系统在系统构建的时候就会进行用户点击等消息的订阅。一些框架和软件产品用XML在初始化注册订阅信息。许多复杂的别的系统则在运行时进行订阅处理 ,例如RSS。

优点

1.低耦合
publisher和subscriber时低耦合的,他们可以继续运行而不用管对方的状态。在传统的客服端-服务器范例中,服务器不运行,客户端就不能发送消息到服务器。客户端不运行,服务器就接收不到消息。
2.可扩展型
通过平行的运行机制,消息缓存机制,基于一定规则的消息发送机,相比通常的客户端-服务端模式,订阅发布模式有更好的扩展性。但是在高耦合大容量的企业级环境中,不适合订阅发布机制。

缺点

低耦合
为什么这么说呢?publisher的消息数据结构必须定义的很合理,有相当的伸缩性。为了更改要发布的消息数据结构,publisher可能要知道所有的subscriber,同时也要修改他们或者与旧版本保持兼容。这使得重构publisher变的很困难。因为随着时间推移可能会更改需求,publisher数据结构的不变性会成为负担。
However, this is a common issue with any client/server architecture and is best served by versioning content payloads or topics and/or changing URL end points for backward compatibility.


我用到这个设计模式是为了解决在ajax回调中,进行组件化的问题。即在需要使用ajax数据的组件中进行订阅事件,在ajax回调中,或者model中进行发布事件,从而降低耦合,同时避免回调地狱。

具体的实现代码如下

/*
 * jQuery global custom event plugin (gevent)
 *
 * Copyright (c) 2013 Michael S. Mikowski
 * (mike[dot]mikowski[at]gmail[dotcom])
 *
 * Dual licensed under the MIT or GPL Version 2
 * http://jquery.org/license
 *
 * Versions
 *  0.1.5 - initial release
 *  0.1.6 - enhanced publishEvent (publish) method pass
 *          a non-array variable as the second argument
 *          to a subscribed function (the first argument
 *          is always the event object).
 *  0.1.7
 *  0.1.8
 *  0.1.9 - documentation changes
 *
*/

/*jslint         browser : true, continue : true,
  devel  : true, indent  : 2,    maxerr   : 50,
  newcap : true, nomen   : true, plusplus : true,
  regexp : true, sloppy  : true, vars     : false,
  white  : true
*/
/*global jQuery */

(function ( $ ) {
  'use strict';
  $.gevent = ( function () {
    //---------------- BEGIN MODULE SCOPE VARIABLES --------------
    var
      subscribeEvent, publishEvent, unsubscribeEvent,
      $customSubMap = {}
      ;
    //----------------- END MODULE SCOPE VARIABLES ---------------

    //------------------- BEGIN PUBLIC METHODS -------------------
    // BEGIN public method /publishEvent/
    // Example  :
    //   $.gevent.publish(
    //     'spa-model-msg-receive',
    //     [ { user : 'fred', msg : 'Hi gang' } ]
    //   );
    // Purpose  :
    //   Publish an event with an optional list of arguments
    //   which a subscribed handler will receive after the event object.
    // Arguments (positional)
    //   * 0 ( event_name )  - The global event name
    //   * 2 ( data )        - Optional data to be passed as argument(s)
    //                         to subscribed functions after the event
    //                         object. Provide an array for multiple
    //                         arguments.
    // Throws   : none
    // Returns  : none
    //
    publishEvent = function ( event_name, data ) {
      var data_list;

      if ( ! $customSubMap[ event_name ] ){ return false; }

      if ( data ){
        data_list = Array.isArray( data ) ? data : [ data ];
        $customSubMap[ event_name ].trigger( event_name, data_list );
        return true;
      }

      $customSubMap[ event_name ].trigger( event_name );
      return true;
    };
    // END public method /publishEvent/

    // BEGIN public method /subscribeEvent/
    // Example  :
    //   $.gevent.subscribe(
    //     $( '#msg' ),
    //     'spa-msg-receive',
    //     onModelMsgReceive
    //   );
    // Purpose  :
    //   Subscribe a function to a published event on a jQuery collection
    // Arguments (positional)
    //   * 0 ( $collection ) - The jQuery collection on which to bind event
    //   * 1 ( event_name )  - The global event name
    //   * 2 ( fn ) - The function to bound to the event on the collection
    // Throws   : none
    // Returns  : none
    //
    subscribeEvent = function ( $collection, event_name, fn ) {
      $collection.on( event_name, fn );

      if ( ! $customSubMap[ event_name ] ) {
        $customSubMap[ event_name ] = $collection;
      }
      else {
        $customSubMap[ event_name ]
          = $customSubMap[ event_name ].add( $collection );
      }
    };
    // END public method /subscribeEvent/

    // BEGIN public method /unsubscribeEvent/
    // Example  :
    //   $.gevent.unsubscribe(
    //     $( '#msg' ),
    //     'spa-model-msg-receive'
    //   );
    // Purpose  :
    //   Remove a binding for the named event on a provided collection
    // Arguments (positional)
    //   * 0 ( $collection ) - The jQuery collection on which to bind event
    //   * 1 ( event_name )  - The global event name
    // Throws   : none
    // Returns  : none
    //
    unsubscribeEvent = function ( $collection, event_name ) {
      if ( ! $customSubMap[ event_name ] ){ return false; }

      $customSubMap[ event_name ]
        = $customSubMap[ event_name ].not( $collection );

      if ( $customSubMap[ event_name ].length === 0 ){
        delete $customSubMap[ event_name ];
      }

      return true;
    };
    // END public method /unsubscribeEvent/
    //------------------- END PUBLIC METHODS ---------------------

    // return public methods
    return {
      publish     : publishEvent,
      subscribe   : subscribeEvent,
      unsubscribe : unsubscribeEvent
    };
  }());
}( jQuery ));


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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 前言我从去年开始使用 RxJava ,到现在一年多了。今年加入了 Flipboard 后,看到 Flipboard...
    占导zqq阅读 9,157评论 6 151
  • 一、 消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合、异步消息、流量削锋等问题。实现高性能...
    步积阅读 56,814评论 10 138
  • “你好,我叫李言蹊。以后我们就做同桌了,请多多指教”她的声音不大,但是很好听,她说起话来爱笑,脸上也会漾出好看的酒...
    柚子寒一阅读 356评论 0 0