代理模式

场景:小明送花记

小明喜欢一群漂亮的女神MM*,所以开始了疯狂的送花行为,开始他自己去送花,发现被别人发现了会影响后面的追求其他女神,所以小明踏上了找代理送花的这条不归路

初识代理

  • 小明追MM送花,直接送
  • 小明选择MM的好朋友(小明同桌)送花
let Flower = function(flowerName) {
  this.flowerName = flowerName
}

let XiaoMing = {
  sendFlower : function (target){
    let flower = new Flower("牡丹");
    target.receiveFlower(flower);
  }
}

const MM = {
  receiveFlower : function (flower){
    console.log("MM收到一束花: " + flower.flowerName)
  }
}

const Frient = {
 receiveFlower : function(flower){
   MM.receiveFlower(flower);
 }
}

XiaoMing.sendFlower(MM)
XiaoMing.sendFlower(Frient)

你会发现结果是一样的,所以这个代理有什么用呢???效果都一样嘛!!!会要转手,多麻烦!!!

别着急,继续看下去就知道了

初见代理作用

  • 小明上次失败了,他觉得是可能是MM心情不好,所以拒绝了他
  • 吸取上次的教训,这次花了多一点钱,找了一个靠谱的代理,朋友2,希望她能在MM2心情好的时候送出花花
let MM2 = {
  receiveFlower : function(flower){
    console.log("MM2收到一束:" + flower.flowerName)
  },
  listenGood: function(fn){
    // 假设1秒后心情变得特别好
    setTimeout(() => {
      fn();
    }, 1000);
  }
}

let Frient2 = {
  receiveFlower:function(flower){
    MM2.listenGood(() => {
      MM2.receiveFlower(flower);
    });
  }
}

XiaoMing.sendFlower(Frient2)

这里可以看出,代理的作用还是很大的,能决定事件触发执行时间(这里更好的是时机)

代理 - 保护模式

  • 小明表白失败了,他觉得是自己买错花了,表白怎么能用牡丹呢?于是买了一束玫瑰准备继续表白
  • 找到了第三个MM3,但是这个MM3的朋友知道小明前面的事迹
  • MM3的朋友觉得小明太渣了,直接拒绝了帮忙送花的事
XiaoMing = {
  sendFlower : function (target){
    let flower = new Flower("玫瑰");
    target.receiveFlower("小明", flower);
  }
}

let MM3 = {
  receiveFlower : function(flower){
    console.log("MM3收到一束:" + flower.flowerName)
  } 
}

let Friend3 = {
   receiveFlower: function(name , flower){
     if(name === "小明"){
       console.log(name + ",你不配给我替你送花")
       return;
     }
     MM3.receiveFlower(flower);
   }
}

XiaoMing.sendFlower(Friend3);

这里可以看到我们可以在外设置关卡,如果说是渣渣,我们不让进入后面部分,这样可以很好的保护内部

  • 小明受不了,“你不帮忙送,怎么不早说”,这接连的失败,然后就委屈巴巴的哭了,嘴里还念叨着:“这是我一个星期的生活费呀!!!”
  • 朋友3:“你以前都没有问我呀,我咋知道呢!!!”
  • 小明:“哦,原来还是我的错!!!”
  • 朋友3看着小明的样子:“看你这样子,只要你耐心改过,不这么花心,我可以考虑帮你一回”
  • 小明答应了,于是又改变了,小明会提前问朋友3是否愿意送花,在决定买花
XiaoMing = { 
  sendFlower : function (target){ 
    target.receiveFlower("小明", function(){
      return new Flower("玫瑰");
    });
  }
}

MM3 = {
  receiveFlower : function(flower){
    console.log("MM3收到一束:" + flower.flowerName)
  } 
}

Friend3 = {
   receiveFlower: function(name , fn){
     if(name === "小明"){
       console.log(name + ",你还不配给我替你送花")
       return;
     }
     // 有送花的资格了
     let flower =  fn();
     MM3.receiveFlower(flower);
   }
}

XiaoMing.sendFlower(Friend3);

上面不难看出,其实我们可以把一些很消耗资源的事情放在我们通过代理后再做,这样就节省了很多不必要的消耗

代理模式 - 虚拟代理

不使用代理的图片预加载

var myImageNoProxy = (function(){
  var imgNode = document.createElement("img");
  document.body.appendChild(imgNode);
  var img = new Image;

  img.onload = function(){ 
    // 模拟加载过程
    setTimeout(() => {
      imgNode.src = img.src;
    }, 1000);
  }

  return {
    setSrc: function(src){
      imgNode.src = "../../work/图片/loadding.jpg";
      img.src = src;
    }
  }

}())

myImageNoProxy.setSrc("../../work/图片/test.png")

使用代理的图片预加载

let myImage = (function(){ 
  let imgNode = document.createElement("img");
  document.body.appendChild(imgNode);
  return {
    setSrc:function(src) {
      imgNode.src = src;
    } 
  }
}())

myImage.setSrc("../../work/图片/test.png")

let proxyMyImage = (function(){

  let img = new Image();

  img.onload = function(){
    // 模拟加载过程
    setTimeout(() => {
      myImage.setSrc(img.src)
    }, 1000); 
  }

  return {
    setSrc:function(src){
      myImage.setSrc("../../work/图片/loadding.jpg")
      img.src = src;
    }
  }
}())

proxyMyImage.setSrc("../../work/图片/test.png")

整体来看,其实两个图片的预加载都差不多,但是代理模式相对会好一点,每个方法都有自己的作用,后面有部分图片不需要预加载的时候只需要更换方法,而无需改变方法内部代码

请求统一上传

场景

点击一下复选框就会上传服务器,但是这样比较消耗性能,所以更改为点击的时候先保存在本地,等到不再操作的时候就一起上传上去

let sendToServer = function(id){
  console.log("开始上传到服务器... id :" + id);
}
 
var proxySendToServer = (function(){
    var cache = [],  // 缓存一段时间内的id列表
        timer = null; // 定时器
    
    return function(id){
      // 缓存id
      cache.push(id);
      
      // 如果上一个timer还在,那么就结束
      if(timer){
        return;
      }

      // 否则设置定时器
      timer = setTimeout(() => { 
         // 真正的上传到服务器
         sendToServer(cache.join(","));
         clearTimeout(timer)
         timer = null;
         cache = [];
      }, 2000);
    }
}())


let checkBoxs = document.getElementsByTagName("input");
for (let index = 0; index < checkBoxs.length; index++) {
  const element = checkBoxs[index];

  element.onclick = function(){
      
    // 使用代理上传到服务器
    if(this.checked === true){ 
      proxySendToServer(this.id);
    }
      
  }
}

代理总结

代理和本体接口的一致性:通过上面的代码可以看到,我们代理和本体都是暴露了相同的接口,代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别

  • 用户可以放心请求代理,他只关心是否得到自己想要的结果

  • 在任何使用本体的地方都可以直接使用代理替代,反之亦然

代理意义:面向对象中有一个设计原则 ----> 单一职责原则【就是一个类(通常也包括一个对象和函数等)而言,应该仅有一个引起它变化的原因,如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能有很多个。面向对象设计鼓励将行为分布到细粒度的对象中,如果一个对象承担太多职责,等于把这些职责耦合到一起,这种耦合会导致脆弱和低内聚的设计】

posted on 2021-02-05 17:00  人生之外的路途  阅读(118)  评论(0编辑  收藏  举报