首页 > 代码库 > 代理模式小试

代理模式小试

一、介绍

什么是代理模式。

惯例,我们先看一下《研磨设计模式》中的介绍——为其他对象提供一种代理以控制对这个对象的访问。代理模式的本质是——控制对象访问

什么意思呢?

就是我们每次访问一个对象的时候,实际上去访问这个对象的代理。这个代理实际上就是这个对象的替身,可以实现原本对象绝大多数的功能。只有当需要某些特殊功能的时候,才去调用原本的对象。这样一来,在不修改原对象的情况下,就可以在代理对象上实现很多特殊的功能。这些功能基本都属于访问控制。

这里所说的代理跟我们平时所说的各种代理,其实就是一个意思。所以说,像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:如果本篇博文您觉得不错的话,请别忘了推荐一下,谢谢。