设计模式之代理模式

现代商业界很流行一种叫代理商的人物,在商业界中代理商又称商务代理,是在其行业管理范围内接受他人委托,为他人促成或缔结交易的一般代理人。很多公司为了迅速占领市场,通常会选用一种叫代理商加盟的商业模式。比如快递公司,很多快递公司都会有代理商网点,发送快递时,会先把快递发到网点,然后网点就会发送到收件人手中。在程序语言中,我们把像这种一个类代表另一个类的功能的设计模式叫做代理模式

代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

主要解决问题:在直接访问对象时带来的问题,比如说:比如对象创建开销很大、或者某些操作需要安全控制,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层(即中间层)

代理模式的实现

我们先用一个快递公司通过代理网点发送快递的情形,抽象出来的程序例子,来理解代理模式:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 快递公司类
class ExpressCompany {
constructor (name) {
this.name = name; // 公司名称
this.proxy = null; // 代理
}

// 设置网点
setProxy (proxy) {
this.proxy = proxy;
}

// 发送
send (target) {
this.proxy.send(target, this.name);
}
}

// 快递公司代理网点类
class ProxyBranch {
constructor(name) {
this.name = name;
}

// 发送
send (target, company) {
company += this.name
target.receive(company);
}
}

// 快递接受者类
class Person {
constructor(name) {
this.name = name
}

// 接收
receive (company) {
console.log(this.name + "接收到了" + company + "发来的快递。");
}
}

var xiaoming = new Person('小明'); // 创建快递接收人小明
var proxy = new ProxyBranch('xxx代理网点'); // 创建xxx代理网点
var company = new ExpressCompany('xxx快递公司'); // 创建xxx快递公司

company.setProxy(proxy); // 快递公司设置代理网点
company.send(xiaoming); // 快递公司发送快递给小明
// => 小明接收到了xxx快递公司xxx代理网点发来的快递。

这段代码中,ExpressCompany类没有直接发送快递给Person类,而是引入了第三者ProxyBranch代理类,代替ExpressCompany类转发快递给Person类,这就是代理模式。

虚拟代理

在上面代码片段中,小明是从一开始就被创建的一个接受者,这就好比代理网点一直都在惦记着要不要给小明发送快递,这是完全没有必要的,网点只需在从快递公司接收到小明的快递时再想起小明就可以了。也就是说,在一开始就把小明这个接受者创建出来是非常的不必要的,因为这样太浪费系统资源了,我们应该换一种方式,在ProxyBranch类执行send()方法时再创建小明这个Person对象的实例。像这样的代理方式叫虚拟代理,虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建

看下面的程序实例,理解什么是虚拟代理:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 快递公司类
class ExpressCompany {
constructor (name) {
this.name = name; // 公司名称
this.proxy = null; // 代理
}

// 设置网点
setProxy (proxy) {
this.proxy = proxy;
}

// 发送
send (target) {
this.proxy.send(target, this.name);
}
}

// 快递公司代理网点类
class ProxyBranch {
constructor(name) {
this.name = name;
}

// 发送
send (target, company) {
var xiaoming = new Person(target); // 创建快递接收人小明

company += this.name
xiaoming.receive(company);
}
}

// 快递接受者类
class Person {
constructor(name) {
this.name = name
}

// 接收
receive (company) {
console.log(this.name + "接收到了" + company + "发来的快递。");
}
}

var proxy = new ProxyBranch('xxx代理网点'); // 创建代理网点
var company = new ExpressCompany('xxx快递公司'); // 创建xxx快递公司

company.setProxy(proxy); // 设置代理网点
company.send('小明'); // 快递公司发送快递给小明
// => 小明接收到了xxx快递公司xxx代理网点发来的快递。

现在把创建接收者延迟到了代理类ProxyBranch的send方法中,实现了虚拟代理。

保护代理

通过上面的代码实例,我们了解到了什么是代理模式,但上面的代理模式可以说是没有任何意义的,而且还无端增加了程序的复杂性。现实中小明有可能无时无刻都在家吗?这是不可能的。那么代理网点的工作人员是不是要等到小明在家的时候才能把快递送到小明的手里?这是肯定的。这样的话,是不是相当于小明被设置了访问权限,只有他在家的时候才能访问。这就是保护代理——控制其他程序对被代理对象的访问

现在,假设小明空闲在家的时间段为: 7:00 - 9:00, 18:00 - 23:00,保护代理的实现如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 快递公司类
class ExpressCompany {
constructor (name) {
this.name = name; // 公司名称
this.proxy = null; // 代理
}

// 设置网点
setProxy (proxy) {
this.proxy = proxy;
}

// 发送
send (target) {
this.proxy.send(target, this.name);
}
}

// 快递公司代理网点类
class ProxyBranch {
constructor(name) {
this.name = name;
}

// 发送
send (target, company) {
var freeTime, h;

h = new Date().getHours();

// 小明空闲在家的时间段: 7:00 - 9:00, 18:00 - 23:00
freeTime = [
[7, 9],
[18, 23]
];

for(var i = 0, l = freeTime.length; i < l; i++){
if(freeTime[i][0] < h && h < freeTime[i][1]){
var xiaoming = new Person(target, freeTime) // 创建快递接收人小明

company += this.name
xiaoming.receive(company);
break;
}
}
}
}

// 快递接受者类
class Person {
constructor(name, freeTime) {
this.name = name;
this.freeTime = freeTime;
}

// 接收
receive (company) {
console.log(this.name + "接收到了" + company + "发来的快递。");
}
}

var proxy = new ProxyBranch('xxx代理网点'); // 创建代理网点
var company = new ExpressCompany('xxx快递公司'); // 创建xxx快递公司

company.setProxy(proxy); // 设置代理网点
company.send('小明'); // 快递公司发送快递给小明
// => 小明接收到了xxx快递公司xxx代理网点发来的快递。

总结

代理模式中代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。保护代理可以控制其他程序对被代理对象的访问。虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建,能有效节约系统资源。