代理模式是什么 ?

代理常用于访问控制。以客户委托律师处理案件的例子说明。

大多数人可能缺乏法律知识。因此,当需要打官司的时候,往往要去律师事务所,委托律师帮忙处理。而律师可以知道客户所有的信息。然后,通过分析这些信息区分出有利和不利信息。当法官让客户辩解时,此时律师可以代理用户将一些有利信息说出。

这样通过律师这一层代理,就比用户容易打胜官司。而这就多亏于律师的访问控制能力。

常见的代理模式有哪些?

虚拟代理

Java设计模式之虚拟代理模式

将创建对象或请求的过程放到对象或请求需要被使用的时候处理。这个就虚拟代理,虚拟代理的实现必须和原来的接口一摸一样。

以图片懒加载为例子。

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

我们加载一张图片很容易实现这种写法。在这个例子中,我们创建了一个dom节点并挂载到DOM树上,并对外提供修改加载图片的连接。但是上边这个写真的很完美吗?想象这样一个场景,用户访问网速特别慢,此时页面加载不出图片就会显示空白。一般情况下,我们会提供一个占位图片,当要显示的图片未加载完成的时候显示。以提升用户友好访问。于是乎,我们写出了如下代码。

const myImage = (function() {
    const imgNode = document.createElement("img");
    document.body.appendChild(imgNode);
    const img = new Image();
    img.onload = function() {
        imgNode.src = this.src;
    }

    return {
        setSrc(src) {
            imgNode.src = src;
            img.src = src;
        }
    };

})();

这个代码很完美吗 ?从功能实现的角度来说确实完美,但网网我们的眼光都关注在当下,却未尝考虑以后会遇到的情况。让我们把目光放长远一点。假设在未来,网速已经能够使得 1Gb 文件秒加载,此时,我们并这远古代码的懒加载完全没有存在的必要了。如果,像上面这种写法,我们就得找到这段代码修改。但是,这又违反了 开放-封闭原则。只能扩展代码而不能修改原有的代码,出现这种情况的原因是,我们并没有遵循 单一职责 设计原则。

单一职责指的类(函数/对象)只需要有一个变量引起其内部变化。而这里存在两个,挂载dom节点,和懒加载图片。这时候,我们就需要 代理模式 帮忙解决问题。

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

const proxyMyImage = (function() {
    const img = new Image();
    img.onload = function() {
        myImage.setSrc(this.src);
    };

    return {
        setSrt(src) {
            img.src = src;
            myImage.setSrc("load.png");
        }
    };
})();

上面这份代码就 满足的 单一职责原则。同时,当我们某天不需要图片懒加载的功能时,可以直接替换成原来的对象即可。

缓存代理

常用见的场景,记忆化搜索、redis缓存、HTTP请求缓存、vue的computed、react的useMemo。

主要思想:以计算多个数的加法为例子,如果传入的参数列表在前面已经被计算过且在缓存中存在计算结果,则不需要再次计算,可以直接从缓存中获取。

const add = (function() {
  const cache = {};
  return function(...args) {
    const key = args.toString();
    if (key in cache) {
      return cache[key];
    }
    // 计算
    const sum = args.reduce((prev, curr) => prev + curr, 0);
    // 缓存
    cache[key] = sum;
  }
})();

上面代码通过cache记录之前的计算结果。通过空间换时间的方法加快计算速度。

保护代理

这个和开头的客户请律师代理案件一样,由律师决定如何为用户辩护。

代理模式和装饰器模式的区别 ?

代理模式:在不改变原有接口的条件下,为原始类添加一个代理类(代理类的中的方法必须和原始类一样),主要目的是为了访问控制,而非加强功能,这是它和装饰器模式的最大区别。

装饰器模式:在不该原始类接口的情况下,对原始类进行增强,并支持多个装饰器一起使用。

代理模式的优点有哪些 ?

单一设计原则的实现

代理和本体接口一致性,方便使用,方便替换。