[js设计模式]行为型设计模式--观察者模式(订阅发布者模式

观察者模式

  • 在《javascript设计模式》这本书中, 在写到观察者模式的时候, 上来就第一句就是, 观察者模式又称发布订阅者模式.

Screen Shot 2021-05-19 at 11.13.43.png

  • 说实话, 作为一个设计模式小白,我不太能分清楚他们到底是不是一种模式, 那就从先实现一个观察者模式开始.
  • 在平时的开发过程中, 使用过EventBus,其实EventBus就是一种观察者模式的实际应用.

初步实现

1. 观察者模式, 需要定一个装注册事件容器,这里使用一个对象, 然后, 需要订阅、发布、取消订阅的三个方法.
class Observer {
    constructor() {
    // 装订阅的容器
        this.message = {}
    }
    // 注册消息。订阅消息
    register(name, fn){}
    // 发布消息
    fire(name,...args){}
    // 取消注册
    remove(name,fn){}
}
  • 上面定义了一个装事件的message和订阅消息的register事件, 发布消息的fire事件,取消订阅的remove事件.
2. 实现register注册/订阅方法
// 注册消息。订阅消息
register(name, fn){
// 如果message中没有需要注册的事件
    if(!this.message.hasOwnProperty(name)){
    // 初始化一个数组,并将这一次的事件放进去
    // 使用数组是因为,一个事件名下面可能会注册多个fn方法
        this.message[name] = [fn]
    }else {
    // 如果已经注册了,那么就将此次的方法push进去
        this.message[name].push(fn)
    }
    // 返回this, 是因为可以进行链式调用
    return this;
}
3. 实现fire发布消息方法

// 发布消息
fire(name,...args){
// 获取注册事件下的方法数组
    const fns = this.message[name]
    console.log(fns)
    // 如果没有对应的方法, 抛出异常
    if(!fns) {throw new Error('该方法未被注册!')}
    // 如果有对应的事件数组
    // 遍历事件数组,并进行调用
    fns.forEach(fn=>{
        fn.call(this, ...args)
    })
     // 返回this, 是因为可以进行链式调用
    return this;
}
4. 实现remove移除订阅方法
// 取消注册
remove(name,fn){
    if(!this.message.hasOwnProperty(name)){
        return new Error('无该注册方法!')
    }
    // 下面两种方式都是可以的

    // 方法一:forEach
    // this.message.forEach((item, index) =>{
    //     if(item === fn) {
    //         this.message.splice(index, 1)
    //     }
    // })

    // 方法二:循环加&& 与运算
    // 通过一次for循环
    for (let i=0; i<fn.length; i++){
        this.message[name][i] === fn && this.message[name].splice(i, 1)
    }
    // 返回this, 是因为可以进行链式调用
    return this;
}

完整代码

class Observer {
    constructor() {
        this.message = {}
    }
    // 注册消息。订阅消息
    register(name, fn){
        if(!this.message.hasOwnProperty(name)){
            this.message[name] = [fn]
        }else {
            this.message[name].push(fn)
        }
        return this;
    }
    // 发布消息
    fire(name,...args){
        const fns = this.message[name]
        console.log(fns)
        if(!fns) {throw new Error('该方法未被注册!')}
        fns.forEach(fn=>{
            fn.call(this, ...args)
        })
        return this;
    }
    // 取消注册
    remove(name,fn){
        if(!this.message.hasOwnProperty(name)){
            return new Error('无该注册方法!')
        }
        // 下面两种方式都是可以的

        // this.message.forEach((item, index) =>{
        //     if(item === fn) {
        //         this.message.splice(index, 1)
        //     }
        // })

        // 通过一次for循环
        for (let i=0; i<fn.length; i++){
            this.message[name][i] === fn && this.message[name].splice(i, 1)
        }
        return this;
    }
}

测试代码

function add(a, b){
    console.log(a + b )
}
function sub(a, b){
    console.log(a - b)
}

const observer = new Observer()
observer.register('add',add) // 注册一个add方法, 返回 3
observer.register('sub',sub).fire('sub',1,2) // 注册并发布sub方法, 返回 -1

observer.fire('hoh',1) // 发布一个没有注册过的方法, 抛出‘Error: 该方法未被注册!’异常

致谢

参考文献《javascript设计模式》张容铭

感谢各位的查阅, 前端菜鸡如有理解错误请各位大佬指出, 让小弟也知道自己的错误, 感谢! 🙏

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值