1.场景模拟
考虑这样一个实际应用:
HR提出,当选择一个部门或者是分公司的时候,要把所有的分公司下的员工显示出来,而且不要翻页,方便进行业务处理,只需要显示姓名即可,但是点击姓名会出现这位员工的详细信息。2.不用模式解决
数据库代码就不写了,总的来说就是用户表和部门表
直接上java代码2.1.描述用户数据的对象
package demo10.proxy.example1;/** * 描述用户数据的对象 */public class UserModel { /** * 用户编号 */ private String userId; /** * 用户姓名 */ private String name; /** * 部门编号 */ private String depId; /** * 性别 */ private String sex; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDepId() { return depId; } public void setDepId(String depId) { this.depId = depId; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString(){ return "userId="+userId+",name="+name+",depId="+depId+",sex="+sex+"\n"; }}
2.2.实现示例要求的功能
package demo10.proxy.example1;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.ArrayList;import java.util.Collection;/** * 实现示例要求的功能 */public class UserManager { /** * 根据部门编号来获取该部门下的所有人员 * * @param depId * 部门编号 * @return 该部门下的所有人员 */ public CollectiongetUserByDepId(String depId) throws Exception { Collection col = new ArrayList (); Connection conn = null; try { conn = this.getConnection(); String sql = "select * from tbl_user u,tbl_dep d " + "where u.depId=d.depId and d.depId like ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, depId + "%"); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { UserModel um = new UserModel(); um.setUserId(rs.getString("userId")); um.setName(rs.getString("name")); um.setDepId(rs.getString("depId")); um.setSex(rs.getString("sex")); col.add(um); } rs.close(); pstmt.close(); } finally { conn.close(); } return col; } /** * 获取与数据库的连接 * * @return 数据库连接 */ private Connection getConnection() throws Exception { Class.forName("oracle.jdbc.driver.OracleDriver"); return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "test", "test"); }}
2.3.客户端
package demo10.proxy.example1;import java.util.*;public class Client { public static void main(String[] args) throws Exception{ UserManager userManager = new UserManager(); Collectioncol = userManager.getUserByDepId("0101"); System.out.println(col); }}
3.问题所在
当一次性访问的数据条数过多,并且每条数据的数据量又很大的情况下,就会消耗较多的内存。那么如何解决这个问题呢?
4.使用代理模式解决
4.1问题思考
其实很简单,我们开始时候只要进行名字的显示,点击名字以后才会进入详细信息,所以,我们可以在之间加一个代理,专门负责控制对某条数据的访问,当点击名字以后,再重新查询一下数据库即可,实际上就是以时间换空间的策略。
4.2代理模式的结构和说明
4.3代理模式示例代码
package demo10.proxy.example2;/** * 抽象的目标接口,定义具体的目标对象和代理公用的接口 */public interface Subject { /** * 示意方法:一个抽象的请求方法 */ public void request();}package demo10.proxy.example2;/** * 具体的目标对象,是真正被代理的对象 */public class RealSubject implements Subject{ public void request() { //执行具体的功能处理 }}package demo10.proxy.example2;/** * 代理对象 */public class Proxy implements Subject{ /** * 持有被代理的具体的目标对象 */ private RealSubject realSubject=null; /** * 构造方法,传入被代理的具体的目标对象 * @param realSubject 被代理的具体的目标对象 */ public Proxy(RealSubject realSubject){ this.realSubject = realSubject; } public void request() { //在转调具体的目标对象前,可以执行一些功能处理 //转调具体的目标对象的方法 realSubject.request(); //在转调具体的目标对象后,可以执行一些功能处理 }}
5.使用代理模式重写示例
5.1定义用户数据对象的接口
package demo10.proxy.example3;/** * 定义用户数据对象的接口 */public interface UserModelApi { public String getUserId(); public void setUserId(String userId); public String getName(); public void setName(String name); public String getDepId(); public void setDepId(String depId); public String getSex(); public void setSex(String sex);}
5.2描述用户数据的对象,没有什么变化
package demo10.proxy.example3;/** * 描述用户数据的对象 */public class UserModel implements UserModelApi{ /** * 用户编号 */ private String userId; /** * 用户姓名 */ private String name; /** * 部门编号 */ private String depId; /** * 性别 */ private String sex; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDepId() { return depId; } public void setDepId(String depId) { this.depId = depId; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString(){ return "userId="+userId+",name="+name+",depId="+depId+",sex="+sex+"\n"; }}
5.3代理对象,代理用户数据对象
package demo10.proxy.example3;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;/** * 代理对象,代理用户数据对象 */public class Proxy implements UserModelApi { /** * 持有被代理的具体的目标对象 */ private UserModel realSubject = null; /** * 构造方法,传入被代理的具体的目标对象 * * @param realSubject * 被代理的具体的目标对象 */ public Proxy(UserModel realSubject) { this.realSubject = realSubject; } /** * 标示是否已经重新装载过数据了 */ private boolean loaded = false; public String getUserId() { return realSubject.getUserId(); } public void setUserId(String userId) { realSubject.setUserId(userId); } public String getName() { return realSubject.getName(); } public void setName(String name) { realSubject.setName(name); } public void setDepId(String depId) { realSubject.setDepId(depId); } public void setSex(String sex) { realSubject.setSex(sex); } public String getDepId() { // 需要判断是否已经装载过了 if (!this.loaded) { // 从数据库中重新装载 reload(); // 设置重新装载的标志为true this.loaded = true; } return realSubject.getDepId(); } public String getSex() { if (!this.loaded) { reload(); this.loaded = true; } return realSubject.getSex(); } /** * 重新查询数据库以获取完整的用户数据 */ private void reload() { System.out.println("重新查询数据库获取完整的用户数据,userId==" + realSubject.getUserId()); Connection conn = null; try { conn = this.getConnection(); String sql = "select * from tbl_user where userId=? "; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, realSubject.getUserId()); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // 只需要重新获取除了userId和name外的数据 realSubject.setDepId(rs.getString("depId")); realSubject.setSex(rs.getString("sex")); } rs.close(); pstmt.close(); } catch (Exception err) { err.printStackTrace(); } finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } public String toString() { return "userId=" + getUserId() + ",name=" + getName() + ",depId=" + getDepId() + ",sex=" + getSex() + "\n"; } private Connection getConnection() throws Exception { Class.forName("oracle.jdbc.driver.OracleDriver"); return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "test", "test"); }}
5.4实现示例要求的功能
package demo10.proxy.example3;import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.util.ArrayList;import java.util.Collection;/** * 实现示例要求的功能 */public class UserManager { /** * 根据部门编号来获取该部门下的所有人员 * * @param depId * 部门编号 * @return 该部门下的所有人员 */ public CollectiongetUserByDepId(String depId) throws Exception { Collection col = new ArrayList (); Connection conn = null; try { conn = this.getConnection(); // 只需要查询userId和name两个值就可以了 String sql = "select u.userId,u.name " + "from tbl_user u,tbl_dep d " + "where u.depId=d.depId and d.depId like ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, depId + "%"); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { // 这里是创建的代理对象,而不是直接创建UserModel的对象 Proxy proxy = new Proxy(new UserModel()); // 只是设置userId和name两个值就可以了 proxy.setUserId(rs.getString("userId")); proxy.setName(rs.getString("name")); col.add(proxy); } rs.close(); pstmt.close(); } finally { conn.close(); } return col; } /** * 获取与数据库的连接 * * @return 数据库连接 */ private Connection getConnection() throws Exception { Class.forName("oracle.jdbc.driver.OracleDriver"); return DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", "test", "test"); }}
5.5客户端测试
package demo10.proxy.example3;import java.util.*;public class Client { public static void main(String[] args) throws Exception{ UserManager userManager = new UserManager(); Collectioncol = userManager.getUserByDepId("0101"); //如果只是显示用户名称,那么不需要重新查询数据库 for(UserModelApi umApi : col){ System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getName()); } //如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库 for(UserModelApi umApi : col){ System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getName()+",所属部门:="+umApi.getDepId()); } }}
6.模式讲解
6.1认识代理模式
代理模式是通过创建一个代理对象,用这个代理去代表真实的对象,客户端得到这个对象以后,对客户端没有什么影响,就跟得到了真实的对象一样。当客户端操作这个代理对象时候,实际上功能还是由真实的对象来完成,只不过是通过代理来操作的,也就是客户端操作代理,代理操作真正的对象。
6.2代理模式调用示意图
6.3思考代理模式
代理模式的本质是:控制对象访问
代理模式通过代理目标对象,把代理对象和插入到客户端和目标对象之间,从而为客户端和目标对象之间引入一定的间接性。这种模式适用于查询某个对象次数较少的情况下,否则会查询多至N+1次