Skip to content

代理模式(Proxy Pattern)

代理模式是一种结构型设计模式, 让你能够提供对象的替代品或其占位符。 代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。

代理模式建议新建一个与原服务对象接口相同的代理类, 然后更新应用以将代理对象传递给所有原始对象客户端。 代理类接收到客户端请求后会创建实际的服务对象, 并将所有工作委派给它。

代理模式的解决方案 代理将自己伪装成数据库对象, 可在客户端或实际数据库对象不知情的情况下处理延迟初始化和缓存查询结果的工作。

代理模式结构

  1. 服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。

  2. 服务 (Service) 类提供了一些实用的业务逻辑。

  3. 代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。

    通常情况下, 代理会对其服务对象的整个生命周期进行管理。

  4. 客户端 (Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

几种常用的代理模式

  • 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,远程代理又称为大使(Ambassador)

  • 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建

  • 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限

  • 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果

  • 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等

设计思路

代理模式核心解决的应该是让对象功能更单一。举个例子,现在公司的老板有个接受信息的接口,那么假如我们需要给老板发送消息,操作应该如下:

js
const boss = {
  getMsg(msg){
    // 处理消息
  }
};

boss.getMsg({
  // 消息内容
});

但事实上我们发送给老板的信息,可能会非常复杂甚至出现虚假无用信息,老板需要花很多时间来处理阅读。众所周知老板的时间是非常宝贵的,因此一般他会请一个秘书来帮忙收集信息,让后由秘书来整理汇报。由此,我们来修改一下现有的代码。

js
const boss = {
  getMsg(msg){
    // 处理消息
  }
};

const secretary = {
  getMsg(msg){
    // 处理消息
    const newMsg = handle(msg);
    boos.getMsg(newMsg);
  }
};

secretary.getMsg({
  // 消息内容
});

现在上方修改后的代码,就是代理模式的结构了。大家可以发现,我们从直接给老板发信息,变成了先给秘书发信息,秘书处理过后,再由秘书给老板发送信息。 这样的变化可以带来以下的好处:

  • 老板的getMsg功能可以变得更加单一,不需要加太多的对信息的处理。
  • 由秘书代理老板之后,秘书可以对信息作更多细节处理。

保护代理

保护代理主要是指,代理实例尽可能地保证目标实例的功能正常。因此通常代理会做一些像数据筛选,格式整理等功能。

js
const secretary = {
  getMsg(msg){
    // ...只把紧急认为给老板
    if(msg.priority !== 'height' ){
      boos.getMsg(msg);
    }
  }
};

虚拟代理

虚拟代理一般是指,把某些大开销的操作放在合适的时候执行。

js
const secretary = {
  getMsg(msg){
    boss.listenMood(function(){
      //    假如这里是一个大开销的操作
      const task =  new Task();
      boos.getMsg(task);
    })
  }
};

缓存代理

缓存代理就很好理解了,就是在代理中加一层缓存。这里假设每次给老板发了信息之后,都会有回复。秘书就可以把老板的回复缓存起来,下次如果有客户问到同样的问题,秘书就可以直接把上次老板的回复给他。

js
const secretary = {
  answers:{},
  getMsg(msg){
    if(this.answers[msg]){
     return this.answers[msg];
    }else{
      const answer = boss.getMsg(msg);
      this.answers[msg] = answer;
    }
  }
};

虚拟代理实现图片预加载

js
// 创建一个本体对象
var myImage = (function(){
  // 创建标签
  var imgNode = document.createElement( 'img' );
  // 添加到页面
  document.body.appendChild( imgNode );
  return {
    // 设置图片的src
    setSrc: function( src ){
      // 更改src
      imgNode.src = src;
    }
  }
})();

// 创建代理对象
var proxyImage = (function(){
  // 创建一个新的img标签
  var img = new Image;
  // img 加载完成事件
  img.onload = function(){
    // 调用 myImage 替换src方法
    myImage.setSrc( this.src );
  }
  return {
    // 代理设置地址
    setSrc: function( src ){
      // 预加载 loading
      myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
      // 赋值正常图片地址
      img.src = src;
    }
  }
})();

proxyImage.setSrc( 'http:// image.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );