设计模式-代理模式

1,226 阅读2分钟

我正在参加「掘金·启航计划」

代理模式,是一种常见的设计模式,它为其他对象提供一种代理以控制对这个对象的访问。在 JavaScript 中,代理模式可以直接使用 ES6 中的 Proxy 对象来实现。也可以像下面的例子中。手动实现代理对象。本文将介绍代理模式的概念。代理对象和本体对象保持接口的一致性,就可以实现代理模式;接下来将以多种方式实现代理模式。

概念

代理模式:它为其他对象提供一种代理以控制对这个对象的访问。代理对象作为被代理对象的中介,可以拦截并处理被代理对象的操作,以及为被代理对象提供额外的功能。

原理

代理模式中的代理对象和被代理对象都必须实现相同的接口,这样任何使用被代理对象的地方都可以用代理对象代替。在 TS 中,我们可以使用抽象类或接口来定义代理和被代理对象的共用接口。

以下是一个使用抽象类来定义代理和被代理对象的共用接口的例子:

Subject为公共接口,代理对象和本体都要实现该接口。这样对外就保持了一致性

abstract class Subject {
  abstract request(): void;
}

class RealSubject extends Subject {
  request() {
    console.log('RealSubject request');
  }
}

class MyProxy extends Subject {
  private realSubject: RealSubject | null = null;
  request() {
    if (this.realSubject === null) {
      this.realSubject = new RealSubject();
    }
    this.realSubject.request();
  }
}

const proxy = new MyProxy();
proxy.request(); // 输出:RealSubject request

以下是一个使用代理实现图片预加载的例子: 我们将分别使用多种语法来实现,这也说明语法实现设计模式的语法并不是唯一的

  1. 这个例子我们直接使用es5的语法来实现
const myImage = (function () {
  const imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  return {
    setSrc(src: string) {
      imgNode.src = src;
    },
  };
})();

const proxyImg = (function () {
  const img = new Image();
  img.onload = () => {
    myImage.setSrc(img.src);
  };
  return {
    setSrc(src: string) {
      myImage.setSrc('../loading.gif');
      img.src = src;
    },
  };
})();

proxyImg.setSrc('http://example.com/image.jpg');

  1. 我们使用ES6的proxy语法来来实现
const myImage = (function () {
  const imgNode = document.createElement('img');
  document.body.appendChild(imgNode);
  return {
    setSrc(src: string) {
      imgNode.src = src;
    },
  };
})();

const proxyImg = new Proxy(myImage, {
  get(target, property) {
      
    return function (src: string) {
      console.log(property)
      target.setSrc('../loading.gif');
      const img = new Image();
      img.onload = () => {
        target.setSrc(img.src);
      };
      img.src = src;
    };
  },
});

proxyImg.setSrc('http://example.com/image.jpg');
  1. 使用ts的语法来实现

abstract class Subject {
  abstract setSrc(src: string): void;
}

class MyImage extends Subject {
  private imgNode: HTMLImageElement;

  constructor() {
    super();
    this.imgNode = document.createElement('img');
    document.body.appendChild(this.imgNode);
  }

  setSrc(src: string) {
    this.imgNode.src = src;
  }
}


class ProxyImage extends Subject {
  private myImage: MyImage | null = null;

  setSrc(src: string) {
    if (this.myImage === null) {
      this.myImage = new MyImage();
    }
    this.myImage.setSrc('../loading.gif');
    const img = new Image();
    img.onload = () => {
      this.?.setSrc(img.src);
    };
    img.src = src;
  }
}

const proxyImg = new ProxyImage();
proxyImg.setSrc('http://example.com/image.jpg');

代理模式还可以实现很多功能,比如vue的实现原理就是代理对象,进行响应式通知;这里就不一一展开了

对其他设计模式有兴趣的同学可以看看这个仓库