一、介绍
什么是代理模式。
惯例,我们先看一下《研磨设计模式》中的介绍——为其他对象提供一种代理以控制对这个对象的访问。代理模式的本质是——控制对象访问。
什么意思呢?
就是我们每次访问一个对象的时候,实际上去访问这个对象的代理。这个代理实际上就是这个对象的替身,可以实现原本对象绝大多数的功能。只有当需要某些特殊功能的时候,才去调用原本的对象。这样一来,在不修改原对象的情况下,就可以在代理对象上实现很多特殊的功能。这些功能基本都属于访问控制。
这里所说的代理跟我们平时所说的各种代理,其实就是一个意思。所以说,像JDK动态代理、CGLib动态代理这些也都是代理模式的一个体现。
关于JDK动态代理和CGLib动态代理的简单演示可以参考一下我的另一篇博文JDK动态代理和CGLib动态代理简单演示
二、我的实现
假设我们有一个user表,对应一个实体类User。它有一个字段是passward。现在我们需要控制这个字段的访问权限,只有它本人才能查看和修改,管理员也只能查看,不能修改。
一般可以采用接口的设计,让代理类和被代理类都实现同一个接口,但是,这里演示为了减少耦合,就不这么做了。如下:
1、很简单的实体类:
1 public class User {
2
3 private String userId;
4 private String userName;
5 private String password;
6
7 public User(String userId, String userName, String password) {
8 super();
9 this.userId = userId;
10 this.userName = userName;
11 this.password = password;
12 }
13
14 public String getUserId() {
15 return userId;
16 }
17
18 public void setUserId(String userId) {
19 this.userId = userId;
20 }
21
22 public String getUserName() {
23 return userName;
24 }
25
26 public void setUserName(String userName) {
27 this.userName = userName;
28 }
29
30 public String getPassword() {
31 return password;
32 }
33
34 public void setPassword(String password) {
35 this.password = password;
36 }
37
38 }
2、很简单的代理类:
1 public class UserProxy {
2
3 User user = null;
4
5 UserProxy(User user) {
6 this.user = user;
7 }
8
9 public String getUserId() {
10 return user.getUserId();
11 }
12
13 public void setUserId(String userId) {
14 user.setUserId(userId);
15 }
16
17 public String getUserName() {
18 return user.getUserName();
19 }
20
21 public void setUserName(String userName) {
22 user.setUserName(userName);
23 }
24
25 // 查看密码做权限控制
26 public String getPassword() {
27 // 判断是否为用户本身,或管理员
28 if (isSelf() || isManager()) {
29 return user.getPassword();
30 } else {
31 System.out.println("对不起," + user.getUserName() + ",您没有足够的权限!");
32 return null;
33 }
34 }
35
36 // 修改密码做权限控制
37 public void setPassword(String password) {
38 // 判断是否为用户本身
39 if (isSelf()) {
40 user.setPassword(password);
41 } else {
42 System.out.println("对不起," + user.getUserName() + ",您没有足够的权限!");
43 }
44 }
45
46 // 权限判断,是否为用户自身
47 private boolean isSelf() {
48 // 一般可以在session中当前用户id,比较。
49 // 这里假设不是用户自身
50 return false;
51 }
52
53 // 权限判断,是否为管理员
54 private boolean isManager() {
55 // 一般可以在session中当前用户id,比较。
56 // 这里假设是管理员
57 return true;
58 }
59 }
3、我们测试一下:
1 public class Test {
2
3 public static void main(String[] args) {
4 User user = new User("001", "张三", "12345");
5 UserProxy proxy = new UserProxy(user);
6 System.out.println("用户名:" + proxy.getUserName());
7 System.out.println("现在查看密码!");
8 System.out.println("用户密码:" + proxy.getPassword());
9 System.out.println("现在修改密码!");
10 proxy.setPassword("54321");
11 }
12 }
如上,实现了简单的权限控制了。
三、虚代理
代理有很多种,如虚代理、远程代理、copy-on-write、保护代理、Cache代理、防火墙代理、同步代理、智能代理等等。
需要仔细了解,可以自行查找相关资料。
不过万变不离其宗,这些代理都是符合代理模式的思想的。
上面我的实现,演示的是保护代理。这里再简要介绍一下虚代理。
什么是虚代理呢?对于创建开销很大的对象,用一个创建开销较小的代理对象代替,一般情况下,这个代理对象足够应付绝大多数用户请求。只有当用户请求原对象的特殊功能时,才会创建原对象。
《研磨设计模式》介绍了一种很常用的实现:
一个数据表有很多字段,通常只需要显示其中几个字段,这种情况下就需要使用虚代理来进行优化了。如下:
1、这里用一个接口来统筹目标对象和代理对象,只有简单的get/set方法,如下:
1 public interface UserModelApi {
2
3 public String getUserId();
4
5 public void setUserId();
6
7 public String getUserName();
8
9 public void setUserName();
10
11 public String getPassname();
12
13 public void setPassname();
14
15 }
2、目标类这里就不列出了,代理类如下:
1 public class MyProxy implements UserModelApi {
2
3 // 数据库初次查询后得到的对象,只有一部分字段。
4 private UserModelApi object = null;
5
6 // 标记是否被加载过,即是否深入查询过。
7 private boolean loaded = false;
8
9 public MyProxy(UserModelApi object) {
10 this.object = object;
11 }
12
13 @Override
14 public String getUserId() {
15 return object.getUserId();
16 }
17
18 @Override
19 public String getUserName() {
20 return object.getUserName();
21 }
22
23 @Override
24 public void setUserId(String userId) {
25 object.setUserId(userId);
26 }
27
28 @Override
29 public void setUserName(String userName) {
30 object.setUserName(userName);
31 }
32
33 @Override
34 public void setPassword(String password) {
35 // 设置属性和查询不同,需要另外的
36 object.setPassword(password);
37 }
38
39 @Override
40 // 这里,当请求得到password的时候,由于传入的UserModelApi对象并未包含这个字段
41 // 所以需要深入查询。
42 public String getPassword() {
43 // 需要判断是否已经装载过了
44 if (!this.loaded) {
45 reload();
46 this.loaded = true;
47 }
48 return object.getPassword();
49 }
50
51 private void reload(){
52 //查询数据库,注入字段到object对象
53 }
54 }
------------------------------------------------------------------------------------------------------------------------------------------------------------
PS:如果本篇博文您觉得不错的话,请别忘了推荐一下,谢谢。