上一篇<<JavaScript 设计模式之工厂模式>>
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问 代理模式的用处(个人理解): 为了保障当前对象的单一职责(相对独立性), 而需要创建另一个对象来处理调用当前对象之前的一些逻辑以提高代码的效率、状态判断等。 代理模式中最常用的是虚拟代理和缓存代理
在现实生活中,可以找到很多代理模式使用的场景。明星都有经纪人作为代理。如果请明星来演出,就要先同他的经纪人沟通,谈好相应的细节与报酬。再交给明星。
需求:公司(Company)通过经纪人(agent)找明星(start)开演唱会
//演唱会
var Concert = function(){}
//公司
var Company = {
askforconcert: function(target){
var concert = new Concert();
target.openconcert(concert )
}
}
//明星
var star = {
openconcert: function(concert){
console.log("明星同意开一场演唱会")
}
}
//经纪人代理
var agent = {
openconcert: function(concert){
star.openconcert(concert)
}
}
//执行
Company.askforconcert(agent); //=>明星同意开一场演唱会
/*这样 company直接把请求发给agent,agent再转给star,这样就完成了一个简单的代理模式
(compan=>agent=>star)*/
//经济人可以帮助 明星过滤掉一些请求,比如 钱不够多或者场地不够好,这种请求可以直接在经纪人出被过滤拒绝掉。
//这种代理就叫做保护代理。
//由经纪人Agent来控制对明星star的访问。
一.虚拟代理
- 如果A 通过 B 送花给C,我们可以在A的时候new 一个 flower传递给代理B,再由B决定什么时候或者是否要再转交个最终的target C。new Flower这个操作可以交给B,B决定可以送花给C的时候再由B做 new Flower的操作。这种模式就叫做虚拟代理。虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建
var Flower = function(){
this.price = 150
}
var a = {
sendflower: function(target){
var flower = new Flower()
target.receiveFlower(flower )
}
}
var b = {
receiveFlower: function(flower){
if(flower.price < 100){
console.log("太便宜了,女神表示一脸嫌弃")
return false
}else{
c.receiveFlower(flower)
}
},
}
var c = {
receiveFlower: function(){
console.log("接受了鲜花")
}
}
1.实现图片预加载
//不使用代理
var preLoadImage = (function(){
var imgNode = document.createElement('img');
document.body.append(imgNode)
var img = new Image();
img.onload = function(){
imgNode.src = img.src
}
return {
setSrc: function(src){
imgNode.src = "loading.gif";
img.src = src;
}
}
})()
//使用代理模式的实现方式
var image = (function(){
var imgNode = document.createElement('img');
document.body.append(imgNode);
return {
setSrc: function(src){
imgNode.src = src;
}
}
})()
//代理
var proxyImage = (function(){
var img = new Image();
img.onload = function(){
// 图片加载完成,正式加载图片
image.setSrc = img.src;
}
return {
setSrc: function(src){
image.setSrc = "loading.gif";
img.src = src;
}
}
})
单一职责原则
单一职责指的是,对一个类而言,应该仅有一个引起他变化的原因。如果一个对象承担了多个原则,就意味着这个对象将变得巨大,引起他变化的原因可能也会有多个。面向对象设计鼓励将行为分不到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些值得耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。(书中93页)
preLoadImage方法,承担了添加img标签,还有预加载两个功能,代码耦合到了一起,当我修改添加标签时,可能会影响到另一部分功能。
而用代理方法重构后,image方法只负责创建标签,设置src,预加载功能交给了proxyImage,解除了耦合的代码,两个功能互不干扰。
2.虚拟代理合并http请求
var syncFile = function(id){
$.ajax({
data: {
id: id
}
})...
}
//绑定事件
for(var i = 0;i<fileObjArr.length;i++){
fileObjArr[i].onclick = function(){
if(this.checked === true){
syncFile(this.id)
}
}
}
这里有个很严重的问题,每点一个都会发送一个ajax请求,性能上,这是一个很大的开销
-
需求: 文件同步穿,选中的文件会被上传到服务器上,解决方法,我们可以 通过一个代理函数,来收集一段时间内的请求,将请求的参数缓存起来,与后端人员协商将选中的id作为一个数组传到后台保存。
var syncFile = function(ids){
$.ajax({
data: {
id: ids
}
})...
}
var proxyFile = (function(){
var cache = [],
timer = null;
return function(id){
cache.push(id);
if(timer){
return
}
timer = setTimeout(function(){
syncFile(cache.join(","))
clearTimeout(timer);
timer = null;
cache = [];
},2000)
}
})()
//绑定事件
for(var i = 0;i<fileObjArr.length;i++){
fileObjArr[i].onclick = function(){
if(this.checked === true){
proxyFile(id)
}
}
}
这样,有选中操作的话,不会频繁触发请求。
二.缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致, 则可以返回前面的运算结果。 示例: 为乘法、加法等创建缓存代理
// 计算乘积
var mult = function(){
var a = 1;
for( var i = 0, l = arguments.length; i < l; i++){
a = a * arguments[i];
}
return a;
};
// 计算加和
var plus = function () {
var a = 0;
for( var i = 0, l = arguments.length; i < l; i++ ){
a += arguments[i];
}
return a;
};
// 创建缓存代理的工厂
var createProxyFactory = function( fn ){
var cache = {}; // 缓存 - 存放参数和计算后的值
return function(){
var args = Array.prototype.join.call(arguments, "-");
if( args in cache ){ // 判断出入的参数是否被计算过
console.log( "使用缓存代理" );
return cache[args];
}
return cache[args] = fn.apply( this, arguments );
}
};
// 创建代理
var proxyMult = createProxyFactory( mult ),
proxyPlus = createProxyFactory( plus );
console.log( proxyMult( 1, 2, 3, 4 ) ); // 输出: 24
console.log( proxyMult( 1, 2, 3, 4 ) ); // 输出: 缓存代理 24
console.log( proxyPlus( 1, 2, 3, 4 ) ); // 输出: 10
console.log( proxyPlus( 1, 2, 3, 4 ) ); // 输出: 缓存代理 10