bojue/bojue.github.io

JavaScript设计模式-观察者模式

bojue opened this issue · 0 comments

bojue commented

JavaScript设计模式-观察者模式

观察者模式又称为发布-订阅者模式,用来处理消息对象和消息观察者之间的耦合关系。

观察者模式的消息对象,一般包括三个主要的方法,分别是消息发布,消息订阅和取消订阅。为了防止消息队列被篡改,需要用到闭包创建私有变量存储消息队列容器。

let Observer = (() => {
    var _message = {};
    return {
        subscribe:() => {}, //订阅
        emit: () => {}, //发布
        remove:() => {} //移除订阅
    }
})()

这段代码摘录自《JavaScript设计模式》,涵盖了观察者模式对象的基本骨架,我们需要页面加载完成的时候创建出观察者对象,所以使用了立即执行函数。

下面我们需要依次实现订阅者模式的主体方法。

消息订阅

订阅者模式,首先需要保证消息对象是可以被订阅的,同时满足多个不同模块订阅同一个消息,我们使用type字段区分不同的订阅模块,使用数组存储消息的订阅队列。

subscribe: (type, fun) =>  {
    if(typeof _message[type] === 'undefined') {
        _message[type] = []
    }
    _message[type].push(fun)
}

发布消息

观察者模式消息的发布,首先需要验证消息类型是否已经被注册过,注册过的消息类型直接添加消息类型对应的消息队列,否则需要先创建该消息类型对应的消息队列,然后添加消息。

emit: (type, param) => {
    if(!_message[type]) {
        return false;
    }

    let obj = {
        type: type,
        args: param
    }

    let len = _message[type].length;
    for(let i=0; i<len; i++) {
        _message[type][i].call(this, obj);
    }
}

取消订阅

与消息发布类似,取消订阅操作需要首先对消息队列的消息列表进行验证,确保该类型的消息队列的存在性,否则没有意义。

remove: (type, fun) => {
    if(!Array.isArray(_message[type])) {
        return false;
    }
    let len = _message[type].length - 1;
    for(i = len; i>= 0;i--) {
        let arr = _message[type];
        if(arr == fun) {
            arr.splice(i, 1)
        }
    }
}

测试

/**
 *  控制台打印的内容:
 *  订阅的消息类型:node, 消息内容:node 设计模式
 *  >>>>>>> 订阅操作Error:没有注册js类型的事件 !!!
 *  >>>>>>> 取消订阅操作Error:没有注册js类型的事件 !!!
 */


let Observer = (() => {
    var _message = {};
    return {
        subscribe: (type, fun) => {
            if(typeof _message[type] === 'undefined') {
                _message[type] = []
            }
            _message[type].push(fun)
        },
        emit: (type, param) => {
            if(!_message[type]) {
                console.error(`>>>>>>> 订阅操作Error:没有注册${type}类型的事件 !!!`);
                return false;
            } 
            let obj = {
                type: type,
                args: param
            }

            let len = _message[type].length;
            for(let i=0; i<len; i++) {
                _message[type][i].call(this, obj);
            }
        },
        remove: (type, fun) => { 
            if(!Array.isArray(_message[type])) {
                console.error(`>>>>>>> 取消订阅操作Error:没有注册${type}类型的事件 !!!`);
                return false;
            }
            let len = _message[type].length - 1;
            for(i = len; i>= 0;i--) {
                let arr = _message[type];
                if(arr[i] === fun) {
                    _message[type].splice(i, 1)
                }
            }
        }
    }
})()

Observer.subscribe('node', val => {
    let data = `订阅的消息类型:${val['type']}, 消息内容:${val['args']['msg']}`;
    console.log(data)
})
Observer.emit('node', {
    msg: 'node 设计模式'
})
Observer.emit('js', {
    msg: 'javaScript 设计模式'
})
Observer.remove('js', val => {
    let data = `订阅的消息类型:${val['type']}, 消息内容:${val['args']['msg']}`;
    console.log(data)
})

因为我们订阅了node类型的事件,所以当发布node事件的时候我么可以观察到该事件,我们订阅队列不包含js事件类型所以观察不到js事件。

订阅者模式可以理解为观察对象的一个函数回调,但是又不完全一样。因为事件观察者模式处理的是观察者和被观察对象一对多的关系,函数回调是观察者模式的一种具体实现,但是函数回调处理的是一对一的关系。

参考

  1. 《JavaScript设计模式》
  2. 菜鸟教程