设计模式之-代理模式

什么是代理模式

代理模式,式如其名——在某些情况下,出于种种考虑/限制,一个对象不能直接访问另一个对象,需要一个第三者(代理)牵线搭桥从而间接达到访问目的,这样的模式就是代理模式。

刘备三顾茅庐的故事

话说遥想刘备当年,...省略一万字,就是赖上了诸葛亮,那个三顾茅庐,终于撩上了诸葛亮。emm。。。这个故事告诉我们什么,只要脸皮厚,男的都能撩走,更别提女的了。额,好像偏题了。。。下面我们用代码去实现这个故事,理解一下什么是代理模式。
按道理正常流程是刘备知道有诸葛亮这么一个神人,直接去拜访就好了。

// 刘备
let bei = {
  // 邀请
  invite(){
    liang.reception('草鞋')
  }
}
// 亮
let liang = {
   // 收到礼物
   reception(gift){
     console.log('亮收到礼物:' + gift)
   }
}
// 调用方法
bei.invite()

傲娇亮那是不可能同意的,内心os,我是你这么容易得到的男人么,然后在家里找了个门童。要见我先和门童谈一谈。

// 刘备
let bei = {
  // 邀请
  invite(){
    mentong.reception('草鞋')
  }
}
// 门童
let mentong = {
  // 接收礼物
  reception(gift){
    console.log('门童收到礼物:' + gift)
    // 给诸葛亮
    liang.reception('草鞋')
  }
}
// 亮
let liang = {
  // 接收礼物
  reception(gift){
    console.log('亮收到礼物:' + gift)
  }
}
// 调用方法
bei.invite()

所以,刘备就只能把礼物给了门童,门童在交给了诸葛亮,然后诸葛亮一看,好家伙,草鞋。。。
到此可以看成一个简单的代理了
诸葛亮收到草鞋后也是无语,然后叫来门童告诉他:“以后呢,送草鞋的,你就不用给我了,自己看着处理就好了”,门童心领神会,表示ojbk

// 刘备
let bei = {
  // 邀请
  invite(){
    mentong.reception('草鞋')
  }
}
// 门童
let mentong = {
  // 接收礼物
  reception(gift){
    console.log('门童收到礼物:' + gift)
    if(gift !== '草鞋'){
      // 给诸葛亮
      liang.reception(gift)
    }
  }
}
// 亮
let liang = {
  // 接收礼物
  reception(gift){
    console.log('亮收到礼物:' + gift)
  }
}
// 调用方法
bei.invite()

这样门童就起到了过滤消息的作用,这样的代理就是保护代理。
然后刘备想了想,光送草鞋也不是事,然后找到书童,悄悄塞了点钱,跟门童说您知道诸葛先生的爱好,我给你钱你买点礼物给卧龙先生吧,门童也是诧异什么时候变聪明了.

// 门童
let mentong = {
  // 接收礼物
  reception(){
    // 拿钱去买礼物
    let book = new Book()
    // 给诸葛亮
    liang.reception(book)
  }
}

在这里就可以看出代理者(门童)起到的作用就是控制刘备访问诸葛亮的条件,只有当刘备不送草鞋改送钱的时候才能见到卧龙先生。

es6里面的proxy

我有个朋友同时是多个优质高端婚恋网站的注册VIP。工作之余,他常常给我们分享近期的相亲情感生活进展。
“你们看,这个妹子头像是不是超可爱!”同事哥这天发掘了一个新的婚介所,他举起手机,朝身边几位疯狂挥舞。
“哥,那是新垣结衣。。。”同事哥的同桌无奈地摇摇头,没有停下 coding 的手。
同事哥恢复了冷静,叹了口气:“这种婚恋平台的机制就是这么严格,一进来只能看到其它会员的姓名、年龄和自我介绍。要想看到本人的照片或者取得对方的联系方式,得先向平台付费成为 VIP 才行。哎,我又要买个 VIP 了。”

我一听,哇,这婚恋平台把代理模式玩挺 6 啊!大家想想,主体是同事 A,目标对象是新垣结衣头像的未知妹子。同事 A 不能直接与未知妹子进行沟通,只能通过第三方(婚介所)间接获取对方的一些信息,他能够获取到的信息和权限,取决于第三方愿意给他什么——这不就是典型的代理模式吗?

用代理模式开一家婚姻介绍所吧

“婚介所”的实现

// 未知妹子
const girl = {
  // 姓名
  name: '小美',
  // 自我介绍
  aboutMe: '...'(大家自行脑补吧)
  // 年龄
  age: 24,
  // 职业
  career: 'teacher',
  // 假头像
  fakeAvatar: 'xxxx'(新垣结衣的图片地址)
  // 真实头像
  avatar: 'xxxx'(自己的照片地址),
  // 手机号
  phone: 123456,
}

婚介所收到了小美的信息,开始营业。大家想,这个姓名、自我介绍、假头像,这些信息大差不差,曝光一下没问题。但是人家妹子的年龄、职业、真实头像、手机号码,是不是属于非常私密的信息了?要想 get 这些信息,平台要考验一下你的诚意了 —— 首先,你是不是已经通过了实名审核?如果通过实名审核,那么你可以查看一些相对私密的信息(年龄、职业)。然后,你是不是 VIP ?只有 VIP 可以查看真实照片和联系方式。满足了这两个判定条件,你才可以顺利访问到别人的全部私人信息,不然,就劝退你提醒你去完成认证和VIP购买再来。

// 普通私密信息
const baseInfo = ['age', 'career']
// 最私密信息
const privateInfo = ['avatar', 'phone']

// 用户(同事A)对象实例
const user = {
    ...(一些必要的个人信息)
    isValidated: true,
    isVIP: false,
}

// 掘金婚介所登场了
const JuejinLovers = new Proxy(girl, {
  get: function(girl, key) {
      if(baseInfo.indexOf(key)!==-1 && !user.isValidated) {
          alert('您还没有完成验证哦')
          return
      }
      
      //...(此处省略其它有的没的各种校验逻辑)
    
      // 此处我们认为只有验证过的用户才可以购买VIP
      if(user.isValidated && privateInfo.indexOf(key) && !user.isVIP) {
          alert('只有VIP才可以查看该信息哦')
          return
      }
  }
})

以上主要是 getter 层面的拦截。假设我们还允许会员间互送礼物,每个会员可以告知婚介所自己愿意接受的礼物的价格下限,我们还可以作 setter 层面的拦截。:

// 规定礼物的数据结构由type和value组成
const present = {
    type: '巧克力',
    value: 60,
}

// 为用户增开presents字段存储礼物
const girl = {
  // 姓名
  name: '小美',
  // 自我介绍
  aboutMe: '...'(大家自行脑补吧)
  // 年龄
  age: 24,
  // 职业
  career: 'teacher',
  // 假头像
  fakeAvatar: 'xxxx'(新垣结衣的图片地址)
  // 真实头像
  avatar: 'xxxx'(自己的照片地址),
  // 手机号
  phone: 123456,
  // 礼物数组
  presents: [],
  // 拒收50块以下的礼物
  bottomValue: 50,
  // 记录最近一次收到的礼物
  lastPresent: present,
}

// 掘金婚介所推出了小礼物功能
const JuejinLovers = new Proxy(girl, {
  get: function(girl, key) {
    if(baseInfo.indexOf(key)!==-1 && !user.isValidated) {
        alert('您还没有完成验证哦')
        return
    }
    
    //...(此处省略其它有的没的各种校验逻辑)
  
    // 此处我们认为只有验证过的用户才可以购买VIP
    if(user.isValidated && privateInfo.indexOf(key)!==-1 && !user.isVIP) {
        alert('只有VIP才可以查看该信息哦')
        return
    }
  }
  
  set: function(girl, key, val) {
 
    // 最近一次送来的礼物会尝试赋值给lastPresent字段
    if(key === 'lastPresent') {
      if(val.value < girl.bottomValue) {
          alert('sorry,您的礼物被拒收了')
          return
      }
    
      // 如果没有拒收,则赋值成功,同时并入presents数组
      girl.lastPresent = val
      girl.presents = [...girl.presents, val]
    }
  }
 
})

参考掘金小册《JavaScript 设计模式核⼼原理与应⽤实践》

posted @ 2021-11-11 12:08  自在一方  阅读(53)  评论(0编辑  收藏  举报