代理模式

设计模式之代理模式Proxy

分类: 设计模式   302人阅读  评论(0)  收藏  举报

目录(?)[+]

1.场景

在一个HR(人力资源)应用项目中客户提出,当选择一个部门或时分公司的时候,要把这个部门或者分公司下的所有员工都显示出来,而且不使用分页,方便他们进行业务处理。在显示全部员工的时候, 只需要显示名称即可,但是也需要提供如下功能:在必要的时候可以选择并查看某位员工的详细信息(user表中的所有字段)。

实现起来也非常简单,只需要查询对应deptid下的user表就可以了。但是实现看起来简单,功能也正确,但是蕴涵了一个比较大的问题。那就是,一次性访问的数据条数过多,而且每条描述的数据量又很大的话,将会消耗较多的内存。而且从用户的角度来说,有很大的随机性。客户有可能访问每一条数据,也有可能一条都不访问。也就是说,一次性访问很多条数据,消耗了大量内存,但是很可能是浪费掉了,客户根本就不会去访问那么多数据,对于每条数据,客户只需要查看姓名而已。

那么该怎么实现,才能既把用户数据的姓名显示出来,而又能节省内存空间?当然还要实现在客户想要看到更多数据的时候,能够正确访问到数据呢?这就是我们接下来要讲的代理模式。

2.解决方案

2.1.代理模式的定义

为其他对象提供一种代理以控制对这个对象的访问。

2.2.代理模式的结构和说明


  • Proxy:代理对象,通常具有如下功能。实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用(代理对象中有具体对象的成员变量),可以在需要的时候调用具体的目标对象。可以控制对具体对象的访问,并可以负责创建和删除它。
  • Subject:具体接口,定义代理和具体对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
  • RealSubject:具体的目标对象,真正实现目标接口要求的功能。

在运行时刻一种可能 的代理结构的对象图如下图所示


也就是在客户的时候见到的时候Subject接口。接口调用代理Proxy,然后代理再调用具体对象RealSubject。

3.使用代理重写实例

(1)用户数据对象接口,就是对用户数据对象属性操作的getter/setter方法。

UserModelApi.java

[java]  view plain copy
  1. public interface UserModelApi {  
  2.     public String getUserId() ;  
  3.     public void setUserId(String userId);  
  4.       
  5.     public String getUserName() ;  
  6.     public void setUserName(String userName) ;  
  7.       
  8.     public String getDeptId() ;  
  9.     public void setDeptId(String deptId);  
  10.       
  11.     public String getSex() ;  
  12.     public void setSex(String sex) ;  
  13.   
  14. }  

(3)接下来定义代理对象

Proxy.java

[java]  view plain copy
  1. import java.sql.Connection;  
  2. import java.sql.PreparedStatement;  
  3. import java.sql.ResultSet;  
  4. import java.sql.SQLException;  
  5. /** 
  6.  * 代理对象,代理用户数据对象 
  7.  */  
  8. public class Proxy implements UserModelApi {  
  9.     /** 
  10.      * 持有被代理的具体的目标对象 
  11.      */  
  12.     private UserModel realSubject = null;  
  13.     private boolean loaded = false;//标志位,表示是否被加载过  
  14.     /** 
  15.      * 构造方法,传入被代理的具体的目标对象 
  16.      * @param realSubject 被代理的具体的目标对象 
  17.      */  
  18.     public Proxy(UserModel realSubject) {  
  19.         this.realSubject = realSubject;  
  20.     }  
  21.     // 代理对象中只保存userId和userName属性,其他属性要从实体对象中获取  
  22.     @Override  
  23.     public String getUserId() {  
  24.         // TODO Auto-generated method stub  
  25.         return realSubject.getUserId();  
  26.     }  
  27.     @Override  
  28.     public void setUserId(String userId) {  
  29.         // TODO Auto-generated method stub  
  30.         realSubject.setUserId(userId);  
  31.     }  
  32.     @Override  
  33.     public String getUserName() {  
  34.         // TODO Auto-generated method stub  
  35.         return realSubject.getUserName();  
  36.     }  
  37.     @Override  
  38.     public void setUserName(String userName) {  
  39.         // TODO Auto-generated method stub  
  40.         realSubject.setUserName(userName);  
  41.     }  
  42.     @Override  
  43.     public String getDeptId() {  
  44.         // TODO Auto-generated method stub  
  45.         // 获取的非proxy对象中拥有的属性,则需要再次查询数据库获取  
  46.         if (!loaded)// 判断数据是否加载过。  
  47.         {  
  48.             reload();  
  49.             this.loaded = true;  
  50.         }  
  51.         return realSubject.getDeptId();  
  52.     }  
  53.     @Override  
  54.     public void setDeptId(String deptId) {  
  55.         // TODO Auto-generated method stub  
  56.         realSubject.setDeptId(deptId);  
  57.     }  
  58.     @Override  
  59.     public String getSex() {  
  60.         // TODO Auto-generated method stub  
  61.         if (!loaded) {  
  62.             reload();  
  63.             this.loaded = true;  
  64.         }  
  65.         return realSubject.getSex();  
  66.     }  
  67.     @Override  
  68.     public void setSex(String sex) {  
  69.         // TODO Auto-generated method stub  
  70.         realSubject.setSex(sex);  
  71.     }  
  72.     /** 
  73.      * 重新查询数据库以获取完整的用户数据 
  74.      */  
  75.     private void reload() {  
  76.         System.out.println("重新查询数据库获取完整的用户数据,userId=="  
  77.                 + realSubject.getUserId());  
  78.         Connection conn = null;  
  79.         try {  
  80.             conn = DBConnection.getInstance().getConnection();  
  81.             String sql = "select * from tbl_user where userid=?";  
  82.             PreparedStatement ps = conn.prepareStatement(sql);  
  83.             ps.setString(1, realSubject.getUserId());  
  84.             ResultSet rs = ps.executeQuery();  
  85.             while (rs.next()) {  
  86.                 //只需要重新获取除了userId和name外的数据  
  87.                 realSubject.setDeptId(rs.getString("deptid"));  
  88.                 realSubject.setSex(rs.getString("sex"));  
  89.             }  
  90.             rs.close();  
  91.             ps.close();  
  92.         } catch (Exception err) {  
  93.             err.printStackTrace();  
  94.         } finally {  
  95.             try {  
  96.                 conn.close();  
  97.             } catch (SQLException e) {  
  98.                 e.printStackTrace();  
  99.             }  
  100.         }  
  101.     }  
  102.     @Override  
  103.     public String toString(){  
  104.         return "userId="+getUserId()+",name="+getUserName()  
  105.         +",depId="+getDeptId()+",sex="+getSex()+"\n";  
  106.     }  
  107. }  
(2)定义了接口,需要让UserModel来实现它。
UserModel.java
[java]  view plain copy
  1. /** 
  2.  * 描述用户数据的对象 
  3.  */  
  4. public class UserModel implements UserModelApi {  
  5.     private String userId;//用户编号  
  6.     private String userName;//用户姓名  
  7.     private String deptId;//部门编号  
  8.     private String sex;//性别  
  9.     public String getUserId() {  
  10.         return userId;  
  11.     }  
  12.     public void setUserId(String userId) {  
  13.         this.userId = userId;  
  14.     }  
  15.     public String getUserName() {  
  16.         return userName;  
  17.     }  
  18.     public void setUserName(String userName) {  
  19.         this.userName = userName;  
  20.     }  
  21.     public String getDeptId() {  
  22.         return deptId;  
  23.     }  
  24.     public void setDeptId(String deptId) {  
  25.         this.deptId = deptId;  
  26.     }  
  27.     public String getSex() {  
  28.         return sex;  
  29.     }  
  30.     public void setSex(String sex) {  
  31.         this.sex = sex;  
  32.     }  
  33.     //重写Object类的toString()方法  
  34.     @Override  
  35.     public String toString(){  
  36.         return "userId="+userId+",name="+userName+",depId="+deptId+",sex="+sex+"\n";  
  37.     }  
  38. }  

(4)定义数据库连接对象

DBConnection.java

[java]  view plain copy
  1. import java.sql.Connection;  
  2. import java.sql.DriverManager;  
  3. import java.sql.SQLException;  
  4.   
  5. public class DBConnection {  
  6.     //数据库连接单例,饿汉式  
  7.     private static DBConnection instance=new DBConnection();  
  8.     //静态代码块,用户注册数据库连接驱动  
  9.     static {  
  10.         try {  
  11.             Class.forName("com.mysql.jdbc.Driver");  
  12.         } catch (ClassNotFoundException e) {  
  13.             // TODO Auto-generated catch block  
  14.             e.printStackTrace();  
  15.         }     
  16.     }  
  17.     //获取数据库连接实例。  
  18.     public static DBConnection getInstance() {  
  19.         return instance;  
  20.     }  
  21.     /** 
  22.      * 获取数据库连接,可以直接通过 
  23.      * DBConnection.getInstance().getConnection(); 
  24.      * 来获取数据库连接 
  25.      */  
  26.     public Connection getConnection() throws SQLException   
  27.     {  
  28.         return DriverManager.getConnection("jdbc:mysql://localhost:3306/test""root""123456");  
  29.     }  
  30. }  
(5)在UserManager对象中,不再对表进行全表查询,而是只吃userid和username字段。
UserManager.java

[java]  view plain copy
  1. import java.sql.Connection;  
  2. import java.sql.PreparedStatement;  
  3. import java.sql.ResultSet;  
  4. import java.util.ArrayList;  
  5. import java.util.Collection;  
  6. /** 
  7.  * 实现示例要求的功能 
  8.  */  
  9. public class UserManager {  
  10.     /** 
  11.      * 根据部门编号来获取该部门下的所有人员 
  12.      * @param depId 部门编号 
  13.      * @return 该部门下的所有人员 
  14.      */  
  15.     public Collection<UserModelApi> getUserById(String deptId) throws Exception {  
  16.         Collection<UserModelApi> col = new ArrayList<UserModelApi>();  
  17.         Connection conn = null;  
  18.         try {  
  19.             conn = DBConnection.getInstance().getConnection();  
  20.             //只需要查询userId和name两个值就可以了  
  21.             String sql = "select u.userid,u.name from tbl_user u ,tbl_dept d where u.deptid=d.deptid and u.deptid LIKE ?";  
  22.             PreparedStatement ps = conn.prepareStatement(sql);  
  23.             ps.setString(1, deptId+"%");  
  24.             ResultSet rs = ps.executeQuery();  
  25.             while (rs.next()) {  
  26.                 //这里是创建的代理对象,而不是直接创建UserModel的对象  
  27.                 Proxy proxy=new Proxy(new UserModel());  
  28.                 //只是设置userId和name两个值就可以了  
  29.                 proxy.setUserId(rs.getString("userid"));  
  30.                 proxy.setUserName(rs.getString("name"));  
  31.                 col.add(proxy);  
  32.             }  
  33.             rs.close();  
  34.             ps.close();  
  35.         } finally {  
  36.             conn.close();  
  37.         }  
  38.         return col;  
  39.     }  
  40. }  
(6)测试客户端
Client.java
[java]  view plain copy
  1. import java.util.Collection;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      * @throws Exception  
  8.      */  
  9.     public static void main(String[] args) throws Exception {  
  10.         // TODO Auto-generated method stub  
  11.         UserManager userManager=new UserManager();  
  12.         Collection<UserModelApi> col=userManager.getUserById("0101");  
  13.         //如果只是显示用户名称,那么不需要重新查询数据库  
  14.         for(UserModelApi umApi : col){  
  15.             System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getUserName());  
  16.         }         
  17.         //如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库  
  18.         for(UserModelApi umApi : col){  
  19.             System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getUserName()+",所属部门:="+umApi.getDeptId());  
  20.         }  
  21.     }  
  22. }  

(7)运行结果:

[java]  view plain copy
  1. 用户编号:=user0001,用户姓名:=张三1  
  2. 用户编号:=user0002,用户姓名:=张三2  
  3. 用户编号:=user0003,用户姓名:=张三3  
  4. 重新查询数据库获取完整的用户数据,userId==user0001  
  5. 用户编号:=user0001,用户姓名:=张三1,所属部门:=010101  
  6. 重新查询数据库获取完整的用户数据,userId==user0002  
  7. 用户编号:=user0002,用户姓名:=张三2,所属部门:=010101  
  8. 重新查询数据库获取完整的用户数据,userId==user0003  
  9. 用户编号:=user0003,用户姓名:=张三3,所属部门:=010102  
(8)总结说明:

如果只是访问用户编号和用户信命的数据,是不需要重新查询数据库的。只有当访问到这两个数据以外的数据的时候,才需要重新查询数据库以获得完整的数据。
(9)1+N次查询

上述实例存在一个潜在的问题,那就是如果客户对每条用户数据都要求查看详细数据的话,那么总的查询数据库的次数会是1+N次。

第一次查询,获得了N条数据的用户编号与姓名,然后展示给客户看。如果这个时候,客户对每条数据都点击查看详细信息的话,那么每一条数据都需要重新查询数据库,那么最后总的查询数据库的次数就是1+N次了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值