代理模式详解

什么是代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。

  • 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。(例如:vue3 的双向数据绑定)

保护代理和虚拟代理

先举个例子:
  现在我们假设小明对女生A进行表白,当A在心情好的时候收到花,小明表白的成功率为60%,当A心情差的时候收到花,小明表白的成功率无限接近于零。
  小明和A刚认识2天,还无法分辨A什么时候心情好,如果不合时宜的把花送给A,被扔掉的可能性很大,但是A的朋友B却很了解A,所以小明只管把花交给B,B会监听A的心情变化,然后选择A心情好的时候将花交给A。

  • 从上面的例子中我们可以简单了解到,代理B可以帮助代理A过滤掉一些请求,比如送花的人太胖,年纪太大或者没有宝马,这种请求可以在代理B直接被拒绝掉,这种叫做保护代理
  • 另外,假设现实中的花价格不非,同样程序世界中的new Flower也是一个代价昂贵的操作,那么我们可以把new Flower的操作交给代理B去执行,代理B会在A心情好的时候再执行new Flower,这是代理模式的另外一种形式,叫做虚拟代理。虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。

虚拟代理实现图片预加载

常见的图片预加载通常是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了以后再把它填充到img节点里面,这种场景很适合使用虚拟代理。

const myImage = (() => {
	const imgNode = document.createElement('img');
	document.body.appendChild( imgNode );
	return {
		setSrc:  src => {
			imgNode.src = src;
		}
	}
})()

const proxyImage = (() => {
	const img = new Image;
	img.onLoad = function() {
		myImage.setSrc( this.src );
	}
	return {
		setSrc:  src => {
			myImage.setSrc( ' .gif' );
			img.src = src;
		}
	}
})()

proxyImage.setSrc("http://xxx.jpg")

代理的意义(简单的说提高代码的可读性和可维护性)

  • 单一职责:一个类(函数或者对象)而言,应该仅有一个引起它可变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它可变化的原因可能会有很多。面向对象设计鼓励将行为很不到细粒度的对象之中,如果一个对象承担了过多的职能,等于把这些职责耦合到一起,这种耦合会导致脆弱和低内聚的设计,当变化发生时,设计可能会遭受到以外的破坏。
  • 在面向对象的程序设计中,大多数情况下,若违反其他任何原则,同时将违反开放封闭原则,如果我们自从网络上获取一些体积很小的图片,或者5年后网速很快不再需要预加载,我们希望删除掉预加载的代码,这时候就不得不改动myImage对象了。
  • 实际上,我们只需要给img节点设置src,预加载图片只是为了显示效果,如果能把这个操作放到另一个对象里面,自然是一个非常好的方法。于是代理的作用就提现出来了,预加载的操作完成之后,把请求重新交给本体的MyImage。
  • 这样我们并没有改变或者增加MyImage的接口,只是通过代理对象,实际上给系统添加了新的行为。这符合开放封闭原则的。给img节点设置src和图片预加载这两个功能,被隔离在两个对象里,他们可以各自变化而不影响对方。就算某天不再需要预加载只需要改成请求本体即可。

代理和本体接口的一致性

  • 简单理解为了方便维护,保证接口的一致性可以在日后的维护中方便替换和取消代理。

虚拟代理合并HTTP请求

  • 在vue和react中很多地方都有体现,延迟合并请求缓解服务器压力,节流防抖。

虚拟代理在惰性加载中的应用

  • 类似我们的分页下拉加载,或者通过按钮点击再加载,以加快加载速度。

缓存代理

  • 缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的结果。

缓存代理的案例----计算乘积( 这里使用了闭包 )

const mult = function() {
	console.log( '开始计算乘积')
	let a = 1;
	for ( let i = 0, i< arguments.length; i++ ){
		a = a * arguments[i]
	}
	return a
}

const proxyMult = (function(){
	let cache = {};
	return function() {
		let args = Array.prototype.join.call( arguments, "," );
		if( args in cache ) {
			return cache[ args ];
		}
		return cache[ args ] = mult.apply( this, arguments );
	}
})()

缓存代理用于ajax 异步请求

  • 这里就是我们常用的分页请求,同一页的数据理论上只需要去后台拉取一次,这些已经拉取到的数据被缓存在某个地方被缓存之后,下次再请求同一页面的时候,便可以直接使用之前的数据.
  • 显然这里也可以引入缓存代理,实现方式和计算乘积的例子差不多,唯一不同的是请求数据是一个异步的操作,我们无法直接把计算结果放到代理的对象的缓存中,而是通过回调的方式。
  • 在react,vue中就是我们常用state或者veux,mobx,redux状态管理工具存储我们的数据。

用高阶函数动态创建代理

  • 通过传入高阶函数这种更加灵活的方式,可以为各种计算方法创建缓存代理。现在这些计算方法被当作参数传入专门用于创建缓存代理的工厂中,这样一来,我们就可以为乘法、加法、余数等创建缓存代理。
const createProxyFactory = function( fn ) {
	let cache = {};
	return function() {
		let args = Array.prototype.join.call( arguments, "," );
		if( args in cache ) {
			return cache[ args ];
		}
		return cache[ args ] = fn.apply( this, arguments );		
 	}
}

cosnt proxyPlus = createProxyFactory( plus )
cosnt proxyPlus = createProxyFactory( mult )

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MaxLoongLvs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值