Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
微信公众号 前端自习课

七、代理模式(Proxy Pattern)

1.概念介绍

代理模式(Proxy Pattern) 为其他对象提供一种代理,来控制这个对象的访问,代理是在客户端和真实对象之间的介质。

装饰者模式1

简单的理解:如我们需要请明星来做广告,我们会先通过联系Ta的经纪人,谈好条件才会给明星签合同。

2.优缺点和应用场景

2.1优点

  • 职责单一且清晰。
  • 保护真实对象。
  • 开闭原则,高拓展性。

2.2缺点

  • 由于在客户端和真实对象间添加代理对象,导致请求处理速度变慢。
  • 实现代理模式需要额外工作,有些代理模式实现起来非常复杂。

2.3应用场景

  • 需要隐藏或保护某个类,则为这个类添加代理。
  • 需要给不同访问者提供不同权限,则在代理类上做判断。
  • 需要为某个类添加功能,如添加日志缓存等,我们可以在代理的类做添加,而不管去改原来封装好的类。

3.基本案例

这里我们以吃午饭问题来学习代理模式。通常情况下,我们会有两种方式解决午饭问题:“去餐厅吃”和“叫外卖”。
去餐厅吃的话,我们就是自己过去吃饭了呗,如果是叫外卖,我们就会通过外卖小哥来拿到午饭才能吃起来。

  • 去餐厅吃(没有使用代理模式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义午饭类 参数 菜名
let Lunch = function(greens){
this.greens = greens;
}
Lunch.prototype.getGreens = function(){
return this.greens;
}
// 定义我这个对象
let leo = {
buy: function(greens){
console.log(`午饭吃${greens.getGreens()}`);
}
}
// 去餐厅吃
leo.buy(new Lunch('青椒炒肉')); // 午饭吃青椒炒肉
  • 叫外卖(有使用代理模式)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义午饭类 参数 菜名
let Lunch = function(greens){
this.greens = greens;
}
Lunch.prototype.getGreens = function(){
return this.greens;
}
// 定义外卖小哥这个对象
let brother = {
buy: function(lunch){
leo.buy(lunch.getGreens());
}
}
// 定义我这个对象
let leo = {
buy: function(greens){
console.log(`午饭吃${greens}`);
}
}
// 叫外卖
brother.buy(new Lunch('青椒炒肉')); // 午饭吃青椒炒肉

并且外卖小哥还会帮我们做一些其他事,比如帮我们带瓶可乐,我们改造brotherleo这2个对象,再看看效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let brother = {
buy: function(lunch){
if(leo.needCola) leo.buyCola();
leo.buy(lunch.getGreens());
}
}

let leo = {
needCola: true,
buy: function(greens){
console.log(`午饭吃${greens}`);
},
buyCola: function(){
console.log(`顺手买瓶可乐!`);
}
}
brother.buy(new Lunch('青椒炒肉'));
// 顺手买瓶可乐!
// 午饭吃青椒炒肉

4.保护代理

还是借用 3.基本案例 的叫外卖的例子,我们现在要实现保护代理,而我们需要外卖小哥为了我们的身体健康,超过晚上9点,就不帮我们买可乐。
还是改造上面买可乐的brother对象代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let brother = {
buy: function(lunch){
let nowDate = new Date();
if(nowDate.getHours() >= 21){
console.log('亲,这么晚不要喝可乐哟!');
}else{
if(leo.needCola) leo.buyCola();
leo.buy(lunch.getGreens());
}
}
}
brother.buy(new Lunch('青椒炒肉'));
// 顺手买瓶可乐!
// 午饭吃青椒炒肉

5.虚拟代理

虚拟代理能把一些开销大的对象,延迟到真正需要的时候才去创建和执行。
我们这里举个图片懒加载的例子:
这个案例参考自JS设计模式-代理模式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 图片加载
let ele = (function(){
let node = document.createElement('img');
document.body.appendChild(node);
return{
setSrc : function(src){
node.src = src;
}
}
})()

// 代理对象
let proxy = (function(){
let img = new Image();
img.onload = function(){
ele.setSrc(this.src);
}
return {
setSrc : function(src){
img.src = src;
ele.setSrc('loading.png');
}
}
})()

proxy.setSrc('example.png');

6.缓存代理

缓存代理是将一些开销大的运算结果提供暂存功能,当下次计算时,参数和之前一直,则将缓存的结果返回:
这个案例参考自JS设计模式-代理模式.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//计算乘积
let mult = function(){
let result = 1;
for(let i = 0; i<arguments.length; i++){
result *= arguments[i];
}
return result;
}

// 缓存代理
let proxy = (function(){
let cache = {};
return function(){
let args = Array.prototype.join.call(arguments, '',);
if(args in cache){
return cache[args];
}
return cache[args] = mult.apply(this,arguments);
}
})();

参考资料

  1. 《JavaScript Patterns》

本部分内容到这结束

Author 王平安
E-mail pingan8787@qq.com
博 客 www.pingan8787.com
微 信 pingan8787
每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
JS小册 js.pingan8787.com
微信公众号 前端自习课

bg