浅谈js中的观察者模式发布订阅模式

60次阅读

共计 2365 个字符,预计需要花费 6 分钟才能阅读完成。

观察者模式是软件设计模式的一种。在此种模式中,一个 目标物件 在它本身的状态改变时主动发出通知,观察者 收到通知从而使他们的状态自动发生变化。

观察者模式

目标 观察者 是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。

下面通过 js 来实现下 观察者模式。首先是目标的构造函数,他有个数组,用于添加观察者。还有个广播方法,遍历观察者数组后调用他们的 update 方法:

class Subject{// 目标类 === 被观察者
    constructor(){this.subjectList = [];// 目标列表
    }
    add(fn){// 注册
        this.subjectList.push(fn)
    }
    notify(context){// 发通知    
        var subjectCount = this.subjectList.length
        for(var i=0; i < subjectCount; i++){this.subjectList[i].update(context)
        }
    }
    // 取消注册
    remove(fn){this.subjectList.splice(this.subjectList.indexOf(fn),1)
    }
}

class Observer{// 观察者类
    update(data){console.log('updata +' + data)
    }
}

var Subject1 = new Subject()// 具体目标 1
var Subject2 = new Subject()// 具体目标 2

var Observer1 = new Observer()// 具体观察者 1
var Observer2 = new Observer()// 具体观察者 2

Subject1.add(Observer1);// 注册 //updata +test1
Subject1.add(Observer2);// 注册 //updata +test1
Subject2.add(Observer1);// 注册 //updata +test2

Subject1.notify('test1')// 发布事件
Subject2.notify('test2')// 发布事件

从上面代码可以看出来,先创建 具体目标 具体观察者 ,然后通过 add 方法把 具体观察者 Observer1、Observer2 注册到 具体目标 中,目标和观察者是 直接联系 起来的,所以具体观察者需要提供 update 方法。在 Subject1 中发通知时,Observer1、Observer2 都会接收通知从而更改状态。

发布 / 订阅模式

观察者模式存在一个问题,目标无法选择自己想要的消息发布,观察者会接收所有消息。在此基础上,出现了
发布 / 订阅 模式,在目标和观察者之间增加一个 调度中心 。订阅者(观察者)把自己想订阅的事件注册到 调度中心,当该事件触发时候,发布者(目标)发布该事件到调度中心,由调度中心统一调度订阅者注册到调度中心的处理代码。

class Public{// 事件通道
    constructor(){this.handlers = {};
    }
    on(eventType, handler) { // 订阅事件
        var self = this;
        if (!(eventType in self.handlers)) {self.handlers[eventType] = [];}
        self.handlers[eventType].push(handler);
        return self ;
    }
    emit(eventType) {// 发布事件(发布事件)
        var self = this;
        var handlerArgs = Array.prototype.slice.call(arguments, 1);
        var length = self.handlers[eventType].length
        for (var i = 0; i < length; i++) {self.handlers[eventType][i].apply(self, handlerArgs);
        }
        return self;
    }
    off(eventType, handler) {    // 删除订阅事件
        var currentEvent = this.handlers[eventType];
        var len = 0;
        if (currentEvent) {
            len = currentEvent.length;
            for (var i = len - 1; i >= 0; i--) {if (currentEvent[i] === handler) {currentEvent.splice(i, 1);
                }
            }
        }
        return self ;
    }
}

// 订阅者
function Observer1(data) {console.log('订阅者 1 订阅了:' + data)
}
function Observer2(data) {console.log('订阅者 2 订阅了:' + data)
}

var publisher = new Public();

// 订阅事件
publisher.on('a', Observer1);
publisher.on('b', Observer1);
publisher.on('a', Observer2);

// 发布事件
publisher.emit('a', '第一次发布的 a 事件');
publisher.emit('b', '第一次发布的 b 事件');
publisher.emit('a', '第二次发布的 a 事件'); 
// 订阅者 1 订阅了: 第一次发布 a 事件
// 订阅者 2 订阅了: 第一次发布 a 事件
// 订阅者 1 订阅了: 第一次发布 b 事件
// 订阅者 1 订阅了: 第二次发布 a 事件
// 订阅者 2 订阅了: 第二次发布 a 事件

可以看出来,订阅 / 发布模式下:订阅和发布是不直接调度的,而是通过调度中心来完成的,订阅者和发布者是互相不知道对方的,完全不存在耦合。

正文完
 0