观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新
观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯
例如生活中,我们可以用报纸期刊的订阅来形象的说明,当你订阅了一份报纸,每天都会有一份最新的报纸送到你手上,有多少人订阅报纸,报社就会发多少份报纸
报社和订报纸的客户就形成了一对多的依赖关系
实现代码如下:
被观察者模式
class Subject {
constructor() {
this.observerList = [];
}
addObserver(observer) {
this.observerList.push(observer);
}
removeObserver(observer) {
const index = this.observerList.findIndex(o => o.name === observer.name);
this.observerList.splice(index, 1);
}
notifyObservers(message) {
const observers = this.observeList;
observers.forEach(observer => observer.notified(message));
}
}
观察者:
class Observer {
constructor(name, subject) {
this.name = name;
if (subject) {
subject.addObserver(this);
}
}
notified(message) {
console.log(this.name, 'got message', message);
}
}
使用代码如下:
const subject = new Subject();
const observerA = new Observer('observerA', subject);
const observerB = new Observer('observerB');
subject.addObserver(observerB);
subject.notifyObservers('Hello from subject');
subject.removeObserver(observerA);
subject.notifyObservers('Hello again');
上述代码中,观察者主动申请加入被观察者的列表,被观察者主动将观察者加入列表
发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在
同样的,订阅者可以表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者存在
实现代码如下:
class PubSub {
constructor() {
this.messages = {};
this.listeners = {};
}
// 添加发布者
publish(type, content) {
const existContent = this.messages[type];
if (!existContent) {
this.messages[type] = [];
}
this.messages[type].push(content);
}
// 添加订阅者
subscribe(type, cb) {
const existListener = this.listeners[type];
if (!existListener) {
this.listeners[type] = [];
}
this.listeners[type].push(cb);
}
// 通知
notify(type) {
const messages = this.messages[type];
const subscribers = this.listeners[type] || [];
subscribers.forEach((cb, index) => cb(messages[index]));
}
}
发布者代码如下:
class Publisher {
constructor(name, context) {
this.name = name;
this.context = context;
}
publish(type, content) {
this.context.publish(type, content);
}
}
订阅者代码如下:
class Subscriber {
constructor(name, context) {
this.name = name;
this.context = context;
}
subscribe(type, cb) {
this.context.subscribe(type, cb);
}
}
使用代码如下:
const TYPE_A = 'music';
const TYPE_B = 'movie';
const TYPE_C = 'novel';
const pubsub = new PubSub();
const publisherA = new Publisher('publisherA', pubsub);
publisherA.publish(TYPE_A, 'we are young');
publisherA.publish(TYPE_B, 'the silicon valley');
const publisherB = new Publisher('publisherB', pubsub);
publisherB.publish(TYPE_A, 'stronger');
const publisherC = new Publisher('publisherC', pubsub);
publisherC.publish(TYPE_C, 'a brief history of time');
const subscriberA = new Subscriber('subscriberA', pubsub);
subscriberA.subscribe(TYPE_A, res => {
console.log('subscriberA received', res)
});
const subscriberB = new Subscriber('subscriberB', pubsub);
subscriberB.subscribe(TYPE_C, res => {
console.log('subscriberB received', res)
});
const subscriberC = new Subscriber('subscriberC', pubsub);
subscriberC.subscribe(TYPE_B, res => {
console.log('subscriberC received', res)
});
pubsub.notify(TYPE_A);
pubsub.notify(TYPE_B);
pubsub.notify(TYPE_C);
上述代码,发布者和订阅者需要通过发布订阅中心进行关联,发布者的发布动作和订阅者的订阅动作相互独立,无需关注对方,消息派发由发布订阅中心负责
两种设计模式思路是一样的,举个生活例子:
上述过程中,如果公司自己去管理快递的配送,那公司就会变成一个快递公司,业务繁杂难以管理,影响公司自身的主营业务,因此使用何种模式需要考虑什么情况两者是需要耦合的
两者区别如下图:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
单个实例,只有一个对象,多次创建,返回同一个对象。单例模式的核心:确保只有一个实例,并提供全局访问
观察者模式又叫发布-订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
//发布者-药店
let drugstore = {
//可在订阅事件的客户列表
clientList: {},
//添加事件订阅对象的方法
listen(eventName, fn) {
if (!this.clientList[eventName]) {
//如果客户订阅的事件当前不存在,则初始化这个事件
this.clientList[eventName] = [];
}
//如果存在,将客户端的联系方法添加到数组中
this.clientList[eventName].push(fn);
},
//向订阅对象发布消息的方法
publish(eventName, data) {
this.clientList[eventName].map(fn => {
//将需要发布的数据作为参数,调用客户在订阅时传入的回调函数
fn(data);
})
}
}
//订阅者-用户
//口罩到货后,小明要做的事件
function xiaoMing(data) {
console.log('口罩到货了,我要赶紧去买一些!');
}
//小明监听口罩到货事件
drugstore.listen('haveMask', xiaoMing);
// 口罩到货后,小红要做的事情
function xiaoHong(data) {
if (new Date() > new Date('2022/8/31')) {
console.log('疫情应该结束了,不买口罩了')
} else {
console.log('赶紧去买口罩,不然就买不着了')
}
}
// 小红监听口罩到货事件
drugstore.listen('haveMask', xiaoHong);
// 口罩到货后,小白要做的事情
function xiaoBai(data) {
if (data.price > 100) {
console.log('这口罩咋这么贵?不买了!')
} else if (data.price > 50) {
console.log('这口罩偏贵,先买10个用着,过段时间看能不能降价')
} else {
console.log('这批口罩价格还可以,买50个屯着')
}
}
// 小白监听口罩到货事件
drugstore.listen('haveMask', xiaoBai)
// 假设到货口罩的相关信息如下
const data = {
type: 'N95',
price: 30,
num: 1000
}
// 发布口罩到货的消息,并把口罩的相关信息作为参数传递给订阅该事件回调函数
// 收到信息后具体怎么处理,完全由回调函数自己定义,“药店”并不关心
drugstore.publish('haveMask', data)
阅读量:1016
点赞量:0
收藏量:0