《Head First Design Patterns》:第11章,代理(Proxy)模式之“保护代理”

定义

这是一种根据访问权限决定客户可否访问对象的代理。比方说,如果你有一个雇员对象,保护代理允许雇员调用对象上的某些方法,经理还可以多调用一些其它的方法(像setSalary()),而人力资源处的雇员可以调用对象上的所有方法。

Javajava.lang.reflect包中支持代理,利用java.lang.reflect包可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法调用转发到你所指定的类。因为实际的代理类是在运行时创建的,我们称之为:动态代理。

我们利用Java的动态代理创建我们的“保护代理”:

  1. Proxy0类是由Java创建的,它实现了完整的Subject接口;
  2. 我们使用XXXInvocationHandler来告诉Proxy0类你要代理的行为,它实现了接口InvocationHandler中的invoke()方法,Proxy0实现的Subject接口的方法全部被转发给XXXInvocationHandler.invoke()

场景

约会服务系统

场景描述

该约会服务系统的要求:

  1. 允许设置和获取一个人的信息;
  2. 不允许用户篡改别人的数据;
  3. 具有评鉴功能,“Hot”表示喜欢对方,“Not”表示不喜欢。

实现方案

服务系统涉及到一个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
public interface Person {
String getName();
String getGender();
String getInterests();
int getGeekRating();

void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setGeekRating(int rating);
}

public class PersonImpl implements Person {
String name;
String gender;
String interests;
int rating;
int ratingCount = 0;

public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getInterests() {
return interests;
}
public int getGeekRating() {
if (ratingCount == 0) return 0;
return (rating / ratingCount);
}

public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setInterests(String interests) {
this.interests = interests;
}
public void setGeekRating(int rating) {
this.rating += rating;
ratingCount++;
}
}

创建两个InvocationHandler类,invoke()中实现具体的代理行为;其中,OwnerInvocationHandler给拥有者使用,NonOwnerInvocationHandler给非拥有者使用。

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class OwnerInvocationHandler implements InvocationHandler {
Person person;

public OwnerInvocationHandler(Person person) {
this.person = person;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {

try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setGeekRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}

public class NonOwnerInvocationHandler implements InvocationHandler {
Person person;

public NonOwnerInvocationHandler(Person person) {
this.person = person;
}

public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {

try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setGeekRating")) {
return method.invoke(person, args);
} else if (method.getName().startsWith("set")) {
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}

实例化Proxy对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
Person getOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}

Person getNonOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnerInvocationHandler(person));
}

参考

  1. https://wickedlysmart.com/head-first-design-patterns/