发布于 

设计模式之代理模式

代理模式简介:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。比如,某对象需访问目标对象,但由于某种情况,不方便或不能直接访问目标对象,通过一个中介进行访问,这个中介就是代理对象。

代理模式结构

  • 抽象接口类:可以是抽象类也可以是接口,代理类和实体类都需要显式实现它。
  • 实体类:被委托的类,是具体业务的执行者。
  • 代理类:负责对实体类的应用,并在实体类处理前后都增加预处理和处理后工作。

例子

模拟企业业务开发,完成每个功能的性能统计

定义一个UserService表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。

定义一个实现类UserServiceImpl实现UserService,并完成相关功能,并统计每个功能耗时。

在不使用代理的情况下,我们是这么做的:

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
//定义一个UserService表示用户业务接口
interface UserService{
//用户登录
login(loginName:string,password:string):void;
//删除用户
deleteUsers():void;
//查询用户
selectUsers():void;

}

//具体业务实现类
class UserServiceImpl implements UserService{

login(loginName: string, password: string): void {

console.log("login性能测试开始。。。")
if(loginName==="admin" && password === "123456"){
console.log("登录成功")
}else{
console.log("账号密码错误")
}
console.log("login性能测试结束")
}

deleteUsers(): void {
console.log("deleteUsers性能测试开始。。。")
console.log("您正在删除用户数据...")
console.log("deleteUsers性能测试结束")
}

selectUsers(): void {
console.log("selectUsers性能测试开始。。。")
console.log("查询了1000个用户数据...")
console.log("selectUsers性能测试结束")
}
}

(() => {
let user = new UserServiceImpl();
user.login("admin","123465");
user.selectUsers();
user.deleteUsers();
})

从上述代码可以看出,在每次进行业务操作时,我们都需要加入相同的代码(代码冗余),不利用代码复用,也违反了单一职责原则,UserServiceImpl本用于进行用户的业务操作,却又加入了其他功能,增加类的复杂度。

下面使用动态代理,将代码改善:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//动态代理
//定义一个UserService表示用户业务接口
interface UserService {
//用户登录
login(loginName: string, password: string): void;
//删除用户
deleteUsers(): void;
//查询用户
selectUsers(): void;

}

//动态代理类
class DynamicProxy {
public static getInstance(user: UserServiceImpl) {
//使用proxy拦截对象
return new Proxy(user, {
get(targetObj, propKey) {
//判断属性是否为方法
if (typeof targetObj[propKey as keyof typeof targetObj] === "function") {
//拦截方法,在每个方法调用前后进行一些处理
return new Proxy(targetObj[propKey as keyof typeof targetObj] as Function, {

apply: function (targetFunc: any, thisArg, argumentsList) {

console.log(propKey as string + "操作性能测试开始")
if (propKey as string === "login") {
targetFunc(argumentsList[0], argumentsList[1])
} else {
targetFunc()
}
console.log(propKey as string + "操作性能测试结束")
}
})
}else{
//如果是值类型就直接返回
return targetObj[propKey as keyof typeof targetObj]
}

}
})
}
}


//具体业务实现类
class UserServiceImpl implements UserService {
val = 13
login(loginName: string, password: string): void {

if (loginName === "admin" && password === "123456") {
console.log("登录成功")
} else {
console.log("账号密码错误")
}
}

deleteUsers(): void {
console.log("您正在删除用户数据...")
}

selectUsers(): void {
console.log("查询了1000个用户数据...")
}
}


(() => {
const user = DynamicProxy.getInstance(new UserServiceImpl())
user.login("ad", "132")
user.deleteUsers()
user.selectUsers()
console.log(user.val)

})()


/*
login操作性能测试开始
账号密码错误
login操作性能测试结束
deleteUsers操作性能测试开始
您正在删除用户数据...
deleteUsers操作性能测试结束
selectUsers操作性能测试开始
查询了1000个用户数据...
selectUsers操作性能测试结束
13
*/

纵观整个程序,我们并没有在 UserServiceImpl 类中添加过多的职责,降低其耦合性,但通过代理对象,为系统添加了新的行为,这是符合开放-封闭原则的。就算有一天我们不再需要对操作进行性能测试,那么只需要把代理对象换成具体实现对象即可。