你不知道的javascript设计模式(八)---- 发布-订阅模式

本文详细讲解了发布-订阅模式在售楼处场景的应用,通过实例演示如何利用JavaScript实现发布者与订阅者之间的解耦,以及取消订阅的机制。同时探讨了模式的优点和潜在问题,提醒读者合理使用以提升代码灵活性。
摘要由CSDN通过智能技术生成

前言

        上一章我们介绍了一种简单但是常见的设计模式,迭代器模式,也自己实现了一遍迭代器对象,这一章节要介绍的内容相信大家或多或少都耳濡目染过,那就是发布-订阅模式

正文

发布-订阅模式的定义

        发布-订阅模式又称观察者模式,它定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖它的对象都会得到通知。前几章我们介绍的设计模式都分离了业务场景的特殊部分,那么发布-订阅模式呢,没错,就如名字所示的那样,发布-订阅模式分离了发布和订阅的功能,让两者间不再需要互相关注对方内部的逻辑

发布-订阅模式的实现

售楼处的例子

        在实现发布-订阅模式前,我们模拟一个场景,假设有一个售楼处,每天会有不同的顾客来订阅楼盘的信息,而售楼的小姐姐会在楼盘的价格信息发生变化的时候,分别通知这些顾客,这就是一个明显的发布-订阅模式的例子。可以发现,这个例子体现了发布-订阅模式的两个明显的优点:

  • 顾客不再需要主动一直向售楼小姐姐询问楼盘的消息,也不需要关注什么时候楼盘发生变化,售楼处会去监听,顾客只负责订阅这份消息
  • 顾客和售楼处的耦合性降低,当有新的购房者出现的时候,只需要把手机号留在售楼处,而售楼处也并不在意用户的其他信息

实现售楼处的例子

        在实现之前,我们先来缕缕这个场景发布-订阅实现的思路,无非就是下面三点:

  • 先创建发布者对象(售楼处)
  • 给发布者对象创建一个缓存数组,用来存放回调函数来通知不同的订阅者(顾客)
  • 当需要发布消息的时候,发布者会遍历这个缓存数组,依次触发里面存放的订阅者回调函数

顺着上面的思路,我们不难封装出下面的售楼处:

 // 定义售楼处
const saleOffices = function() {
	this.clientList = []; 	// 缓存数组
	this.listen = function(fn) {		// 增加订阅者
		this.clientList.push(fn);	// 订阅的消息添加进缓存列表
	}
	this.trigger = function() { 	// 发布消息
		for(let i = 0; i < this.clientList.length; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
}

        这样我们就已经实现好了一个简单的发布-订阅模式,但是上面的实现还并不是完美的,还存在一些问题,并没有对订阅定制化,这样会导致可能你没订阅这个消息也会发布消息,比如小陈只想订阅2号楼盘的价格变化,按上面的写法不管是什么楼盘都会去给小陈发布通知消息,所以我们需要加一个标志的key,让订阅者只收到他们感兴趣的内容

 // 定义售楼处
const saleOffices = function() {
	this.clientList = {};	// 缓存列表
	this.listen = function(key, fn) {		// 增加订阅者
		if (!this.clientList[key]) {
			this.clientList[key] = [];
		}
		this.clientList[key].push(fn);	// 订阅的消息添加进缓存列表
	}
	this.trigger = function() { 	// 发布消息
		// 这里使用arguments的原因是因为第一参数是key,后面可能还有一些发布的参数需要一起给订阅者
		let key = Array.prototype.shift.call(arguments), 
			fns = this.clientList[key], 
			len = fns.length;
		if (!fns || len === 0) {
			return false;
		}
		for(let i = 0; i < len; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
}

        这样就好多了,顾客终于可以订阅自己感兴趣的部分了,售楼处也会根据用户的兴趣去发布对应的消息给顾客!

取消订阅的事件

        假设有一天小陈又不感兴趣这个楼盘了,或者已经买好了,但是售楼处还是照常给小陈发短信,小陈想取消掉之前他订阅的事件应该怎么办呢,所以我们为售楼处额外再封装一个remove方法,用于订阅事件的取消

const saleOffices = function() {
	this.clientList = {};	
	this.listen = function(key, fn) {		
		if (!this.clientList[key]) {
			this.clientList[key] = [];
		}
		this.clientList[key].push(fn);	
	}
	this.trigger = function() { 	
		let key = Array.prototype.shift.call(arguments), 
			fns = this.clientList[key], 
			len = fns.length;
		if (!fns || len === 0) {
			return false;
		}
		for(let i = 0; i < len; i++) {
			let fn = this.clientList[i];
			fn.apply(this, arguments);
		}
	}
	// 取消订阅的事件
	this.remove = function (key, fn) {
		let fns = this.clientList[key];
		if (!fns) {	// 如果key订阅的消息没有被人订阅,则直接返回
			 return false;
		}
		if (!fn) {	// 如果传入的事件未定义,则认为是取消所有事件
			fns && (fns.length = 0);
		} else {
			// 注意要倒序遍历,正序遍历删除节点会导致index错乱,导致后序的删除出错
			for ( let i = fns.length - 1; i >= 0; i--) {
				let _fns = fns[i];
				if ( _fns = fns ) {
					fns.splice(i, 1); // 删除订阅者的回调函数
				}
			}
		}
}

javascript中实现发布-订阅模式的便利性

        在java中实现发布-订阅模式,通常会把订阅者本身当作引用传入发布者对象中,同时,订阅者对象还需提供一个名为诸如update的方法,供发布者调用。而在javascript中我们可以直接使用回调函数来代替传统发布-订阅者模式,显得更加优雅
        值得一提的是,vue中的双向绑定源码也是使用发布-订阅者模式去实现的,感兴趣的同学可以到网上搜相关源码去阅读

小结

        这一章我们给大家介绍了发布-订阅模式,也称观察者模式,发布-订阅模式有着明显的优势:一、减少了程序时间上的耦合;二、减少了各模块间的耦合;可以应用于异步等场景,可以帮助我们编写更加松散耦合的代码
        但是发布-订阅模式也有着它的缺陷,创建订阅者会消耗一定的内存,并且当消息一直未发布的时候,订阅者也会一直存在,对内存会有一定的损耗,并且大量的订阅者和发布者交错在一起,也会导致bug的难以追寻踪迹,难以判断其源头,所以也不能滥用发布-订阅模式
       小伙伴们今天的学习就到这里了,如果觉得本文对你有帮助的话,欢迎转发,评论,收藏,点赞!!!
       每天学习进步一点点,就是领先的开始。如果想继续提高,欢迎关注我,或者关注公众号”祯民讲前端“。大量前端技术文章,面试资料,技巧等助你更进一步!
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值