8

发布-订阅者(观察者模式)以及 代理模式

 3 years ago
source link: https://segmentfault.com/a/1190000040123318
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

发布-订阅者(观察者模式)以及 代理模式

发布于 24 分钟前

它定义了对象一对多的关系,让多个观察者对象同时监听某一个主题。当依赖的对象发生改变时候,所有依赖于他的对象,都会得到通知

  • 支持简单的广播通信,当对象状态发生改变时,会自动通知已经订阅过的对象。
  • 发布者与订阅者耦合性降低,发布者只管发布一条消息出去,它不关心这条消息如何被订阅者使用,同时,订阅者只监听发布者的事件名,只要发布者的事件名不变,它不管发布者如何改变
  • 创建订阅者需要消耗一定的时间和内存。如果过度使用的话,反而使代码不好理解及代码不好维护等等。
  • 比如我们常用的events.

    import EventEmitter from 'events';
     EventEmitter.addListener('ResetjoblistNum', () => {
        this.pageNum = 1
        this.flag = true
      })
     
     EventEmitter.emit('ResetjoblistNum', '') 
      
  • 给发布者添加一个缓存列表,用于存放回调函数来通知订阅者(比如上面的买家收藏了卖家的店铺,卖家通过收藏了该店铺的一个列表名单)。
  • 最后就是发布消息,发布者遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
var Event = (function(){
    var list = {},
          listen,
          trigger,
          remove;
          listen = function(key,fn){
            if(!list[key]) {
                // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
                list[key] = [];
            }
            list[key].push(fn); // 订阅消息添加到缓存列表
        };
        trigger = function(){
            var key = Array.prototype.shift.call(arguments), // 取出消息类型名称
                 fns = list[key]; // 取出该消息对应的回调函数的集合
            // 如果没有订阅过该消息的话,则返回
            if(!fns || fns.length === 0) {
                return false;
            }
            for(var i = 0, fn; fn = fns[i++];) {
                fn.apply(this,arguments); // arguments 是发布消息时附送的参数
            }
        };
        remove = function(key,fn){
            // 如果key对应的消息没有订阅过的话,则返回
            var fns = list[key];
            // 如果没有传入具体的回调函数,表示需要取消key对应消息的所有订阅
            if(!fns) {
                return false;
            }
            if(!fn) {
                fns && (fns.length = 0);
            }else {
                for(var i = fns.length - 1; i >= 0; i--){
                    var _fn = fns[i];
                    if(_fn === fn) {
                        fns.splice(i,1);// 删除订阅者的回调函数
                    }
                }
            }
        };
        return {
            listen: listen,
            trigger: trigger,
            remove: remove
        }
})();
// 测试代码如下:
Event.listen("color",function(size) {
    console.log("尺码为:"+size); // 打印出尺码为42
});
Event.trigger("color",42);

2.代理模式

一个对象,跟本体对象具有相同的接口,以达到对本地对象的访问控制
本地对象只注重业务逻辑的实现,代理控制本地对象的实例化。

简单的来说

  • 客户-主体
  • 客户-代理-主体

比如广告找明星代言,广告商只能联系他的经纪人,经纪人会把商业演出的细节和报酬谈好了,再把合同给到明星来签署

  • 代理对象可以代替本体对象被实例化,此时本体对象未真正实例化,等到合适时机再实例化。
  • 虚拟代理,把开销最大的对象,延迟到真正需要它的时候再做创建
  • 保护代理,AOP 切面思想,先做过滤,再做操作。
// 虚拟代理
var myImage = (function () {
  var imgNode = document.createElement('img')
  document.body.appendChild(imgNode)
  return {
    setSrc: function (src) {
      imgNode.src = src
    },
  }
})()
// 代理模式
var ProxyImage = (function () {
  var img = new Image()
  img.onload = function () {
    setTimeout(() => {
      myImage.setSrc(this.src)
    }, 2000)
  }
  return {
    setSrc: function (src) {
      myImage.setSrc(
        'http://img.lanrentuku.com/img/allimg/1212/5-121204193R0.gif'
      )
      img.src = src
    },
  }
})()
export default ProxyImage

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK