代理模式小试
一、介绍
什么是代理模式。
惯例,我们先看一下《研磨设计模式》中的介绍——为其他对象提供一种代理以控制对这个对象的访问。代理模式的本质是——控制对象访问。
什么意思呢?
就是我们每次访问一个对象的时候,实际上去访问这个对象的代理。这个代理实际上就是这个对象的替身,可以实现原本对象绝大多数的功能。只有当需要某些特殊功能的时候,才去调用原本的对象。这样一来,在不修改原对象的情况下,就可以在代理对象上实现很多特殊的功能。这些功能基本都属于访问控制。
这里所说的代理跟我们平时所说的各种代理,其实就是一个意思。所以说,像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:如果本篇博文您觉得不错的话,请别忘了推荐一下,谢谢。