观察者模式
所谓观察者模式,其实就是为了实现松耦合(loosely coupled)。
在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应
╭─────────────╮ Fire Event ╭──────────────╮
│ │─────────────>│ │
│ Subject │ │ Observer │
│ │<─────────────│ │
╰─────────────╯ Subscribe ╰──────────────╯
实现:
class Subject {
constructor() {
this.observers = [];
}
add(observer) {
this.observers.push(observer);
}
notify(...args) {
this.observers.forEach(observer => observer.update(...args));
}
}
class Observer {
update(...args) {
console.log('do something');
}
}
const sub = new Subject(); /* 系统 */
sub.add(new Observer()); /* 张三点了预约 */
sub.add(new Observer()); /* 李四点了预约 */
sub.notify(); /* 双十一了,通知所有点了预约的人来抢货了 */
发布订阅模式
发布订阅模式属于广义上的观察者模式
发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式
╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮
│ │ Publish Event │ │───────────────>│ │
│ Publisher │────────────────>│ Event Channel │ │ Subscriber │
│ │ │ │<───────────────│ │
╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
在发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件
实现:
class SubPub {
constructor() {
this.observers = {};
}
subscribe(topic, callback) {
if (!this.observers[topic]) {
this.observers[topic] = [];
}
this.observers[topic].push(callback);
}
publish(topic, ...args) {
let callbacks = this.observers[topic] || [];
callbacks.forEach(cb => cb.call(this, ...args));
}
}
const subject = new SubPub(); /* 中介 */
subject.subscribe('两室一厅', () => { console.log('张三'); }); /* 张三想买一个两室一厅的房子 */
subject.subscribe('三室一厅', () => { console.log('李四') }); /* 李四想买一个三室一厅的房子 */
subject.subscribe('两室一厅', () => { console.log('王五'); }); /* 王五想买一个两室一厅的房子 */
subject.publish('两室一厅'); /* 当出现一个两室一厅的房子时,中介会通知张三和王五来看房 */
使用:
class EventEmitter {
constructor() {
this.events = {};
}
// 实现订阅
on(type, callBack) {
if (!this.events[type]) {
this.events[type] = [callBack];
} else {
this.events[type].push(callBack);
}
}
// 删除订阅
off(type, callBack) {
if (!this.events[type]) return;
this.events[type] = this.events[type].filter((item) => {
return item !== callBack;
});
}
// 只执行一次订阅事件
once(type, callBack) {
function fn() {
callBack();
this.off(type, fn);
}
this.on(type, fn);
}
// 触发事件
emit(type, ...rest) {
this.events[type] &&
this.events[type].forEach((fn) => fn.apply(this, rest));
}
}
// 使用如下
// const event = new EventEmitter();
// const handle = (...rest) => {
// console.log(rest);
// };
// event.on("click", handle);
// event.emit("click", 1, 2, 3, 4);
// event.off("click", handle);
// event.emit("click", 1, 2);
// event.once("dbClick", () => {
// console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");
网友评论