现在的位置: 首页 > 综合 > 正文

设计模式12—设计模式之代理模式(Proxy)(结构型)

2013年08月01日 ⁄ 综合 ⁄ 共 7844字 ⁄ 字号 评论关闭
文章目录

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 Collection<UserModel> getUserByDepId(String depId) throws Exception {
		Collection<UserModel> col = new ArrayList<UserModel>();
		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();
		Collection<UserModel> col = 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 Collection<UserModelApi> getUserByDepId(String depId) throws Exception {
		Collection<UserModelApi> col = new ArrayList<UserModelApi>();
		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();
		Collection<UserModelApi> col = 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次

抱歉!评论已关闭.