js设计模式之观察者模式和发布订阅模式

观察者模式

观察者模式:定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知。

  • 一个目标者对象 Subject,拥有方法:添加 / 删除 / 通知 Observer;

  • 多个观察者对象 Observer,拥有方法:接收 Subject 状态变更通知并处理;

目标对象 Subject 状态变更时,通知所有 Observer。

代码实现:

class Subject {
    constructor() {
        this.observers = []; //观察者列表
    }
    //添加
    add(observer) {
        this.observers[this.observers.length] = observer;
    }
    //删除
    remove(observer) {
        let idx = this.observers.findIndex(item = > item === observer);
        idx > -1 && this.observers.splice(idx, 1);
    }
    //通知
    notify() {
        for (let observer of this.observers) {
            observer.update();
        }
    }
}
//观察者类
class Observer {
    constructor(name) {
        this.name = name;
    }
    // 目标对象更新时触发的回调
    update() {
        console.log(`i am ${this.name}`);
    }
}
//实例化目标者
let subject = new Subject();
//实例化两个观察者
let obs1 = new Observer('前端');
let obs2 = new Observer('后端');
//向目标者添加观察者
subject.add(obs1);
subject.add(obs1);
// 目标者通知更新
subject.notify();

优势:目标者与观察者,功能耦合度降低,专注自身功能逻辑;观察者被动接收更新,时间上解耦,实时接收目标者更新状态。

缺点:观察者模式虽然实现了对象间依赖关系的低耦合,但却不能对事件通知进行细分管控,如 “筛选通知”,“指定主题事件通知”

发布订阅模式

发布订阅模式:基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象。

发布订阅模式与观察者模式的不同,“第三者” (事件中心)出现。目标对象并不直接通知观察者,而是通过事件中心来派发通知。

代码实现:

//事件中心
let pubSub = {
    list: {},
    subscribe: function (key, fn) {     //订阅
        if (!this.list[key]) {
            this.list[key] = [];
        }
        this.list[key][this.list[key].length] = fn;
    },
    publish: function (key,...arg){      //发布
        for (let fn of this.list[key]) {
            fn.call(this,...arg);
        }
    },
    unSubscribe: function(key, fn) {     //取消订阅
        let fnList = this.list[key];
        if (fnList) return false;
        if (!fn) {                       //不传入指定取消的订阅方法,则清空所有key下的订阅
            fnList && (fnList.length = 0);
        } else {
            fnList.forEach((item, index) => {
                if(item === fn){
                  fnList.splice(index, 1);
                }
            })
        }
    }
}
​
// 订阅
pubSub.subscribe('onwork', time = > {
    console.log(`上班了:${time}`);
})
pubSub.subscribe('pullwater', time = > {
    console.log(`划水了:${time}`);
})
pubSub.subscribe('offwork', time = > {
    console.log(`下班了:${time}`);
})
//发布
pubSub.publish('pullwater', '12:00:00');
pubSub.publish('offwork', '18:00:00');
//取消订阅
pubSub.unSubscribe('onwork');

观察者模式即发布订阅模式

       很多人都会因为上述的区别(认为发布订阅模式多一个事件中心处理订阅发布)从而得出这是两种模式,其实观察者模式就是发布订阅模式,只是上述的第二段代码比第一段代码实现这种模式实现的更好一些,弥补了第一段代码的缺点。模式可以由多种多样的代码来实现,其核心还都是定义了对象间一种一对多的依赖关系,当目标对象 Subject 的状态发生改变时,所有依赖它的对象 Observer 都会得到通知。

模式的应用

1、 DOM 事件监听就是 “发布订阅模式” 的典型应用:

//事件
let loginBtn = document.getElementById('#loginBtn');
//监听回调函数(指定事件)
function notifyClick(){
    console.log('我被点击了)
}
//添加事件监听
loginBtn.addEventListener('click', notifyClick);
//触发点击,事件中心派发指定事件
loginBtn.click();
//取消事件监听
loginBtn.removeEventlistener('click', notifyClick);

2、jQuery 的 on 和 trigger,$.callback();

// 自定义事件,自定义回调
var callbacks = $.Callbacks() // 注意大小写
callbacks.add(function (info) {
    console.log('fn1', info)
})
callbacks.add(function (info) {
    console.log('fn2', info)
})
callbacks.add(function (info) {
    console.log('fn3', info)
})
//添加完后统一触发。
callbacks.fire('gogogo')

3、Vue 的双向数据绑定;

  • Observer:观察者,这里的主要工作是递归地监听对象上的所有属性,在属性值改变的时候,触发相应的watcher。

  • Watcher:订阅者,当监听的数据值修改时,执行响应的回调函数(Vue里面的更新模板内容)。

  • Dep:订阅管理器,连接Observer和Watcher的桥梁,每一个Observer对应一个Dep,它内部维护一个数组,保存与该Observer相关的Watcher

/* 实现数据监听器(数据劫持)*/
    function Observer(obj, key, value) {
        var dep = new Dep();
        if (Object.prototype.toString.call(value) == '[object Object]') {
            Object.keys(value).forEach(function(key) {
            new Observer(value, key, value[key])
         })
     };
​
    Object.defineProperty(obj, key, {
         enumerable: true,
         configurable: true,
          get: function() {
             if (Dep.target) {
             dep.addSub(Dep.target);
             };
             return value;
          },
        set: function(newVal) {
            value = newVal;
            dep.notify();
            }
        })
    }
    
    // 订阅器
    function Dep() {
        this.subs = [];
        this.addSub = function (watcher) {
            this.subs.push(watcher);
        }
​
        this.notify = function() {
            this.subs.forEach(function(watcher) {
                watcher.update();
            });
        }
    }
​
    // 观察者
   function Watcher(fn) {
        this.update = function() {
            Dep.target = this;
            fn();
            Dep.target = null;
        }
        this.update();
    }
​
    // 连接器
    
输入的值为:{{text}}

4、Vue 的父子组件通信 $on/$emit

5、promise

var src = 'http://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
  console.log('success 1')
}, function () {    
  console.log('failed 1')
})
// then就是一个观察者,等待前一个处理完成

 

你可能感兴趣的:(js设计模式之观察者模式和发布订阅模式)