`

设计模式——代理模式

 
阅读更多

 

 

1 初识代理模式:

 

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

结构:



 

 

参考实现:

 

 

 

2 体会代理模式:

 场景问题: 查询某部门下员工基本信息,如果想看详细,则点击查看

不使用模式下的解决方案:

    select person.* , dept.* from person, dept where person.deptid = dept.id and dept.id = ?

使用模式的解决方案:

    1+N次查询:

     select person.name, person.id from person, dept where person.deptid = dept.id and dept.id = ?

    将最常用的字段 查询出来,放在代理对象中,

    代理对象引用真实对象,将代理对象中需要查看的真实对象的 getXX setXX 通过再次查询数据库得到后放在代理对象中 eg: 查询 sex age  location等

    这就是1+N次查询,N表示有多少个bean对象,就有可能点击每一个实体对象查看详细,这时就需要重新连接数据库查询N次,不过这是个概率问题,不可能得到N次这么多次, 这是明显是花销多时间,少空间的做法,所有的字段只有在需要的时候才会被加载,第一次加载的都是最少的字段;(主要为了节约内存考虑,hibernate的 lazy load也是用这种思路)

 

参考代码如下: 如下代理是 虚代理(虚拟真实对象)

 

package cn.javass.dp.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);
}


package cn.javass.dp.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";
	}
}


package cn.javass.dp.proxy.example3;

import java.sql.*;
import java.util.*;

/**
 * 代理对象,代理用户数据对象
=================主要是看代理对象引用真实对象  和 代理对象中 relaod()写法
 */
public class Proxy implements UserModelApi{
	/**
	 * 持有被代理的具体的目标对象
	 */
	private UserModel realSubject=null;
	/**
	 * 构造方法,传入被代理的具体的目标对象
	 * @param realSubject 被代理的具体的目标对象
	 */
	public Proxy(UserModel realSubject){
		this.realSubject = realSubject;
	}
	/**
	 * 标示是否已经重新装载过数据了  针对字段 depId 和 sex而言
	 */
	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");
	}
}


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");
	}
}


public class Client {
	public static void main(String[] args) throws Exception{
		UserManager userManager = new UserManager();
		Collection<UserModelApi> col = userManager.getUserByDepId("0101");

		//如果只是显示用户名称,那么不需要重新查询数据库  有效减少查询DB需要的内存 时间   典型的以时间换空间的做法
		for(UserModelApi umApi : col){
			System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getName());
		}
		//如果访问非用户编号和用户姓名外的属性,那就会重新查询数据库, 此时使用代理的包装方法来获取 其余属性	
		for(UserModelApi umApi : col){
			System.out.println("用户编号:="+umApi.getUserId()+",用户姓名:="+umApi.getName()+",所属部门:="+umApi.getDepId());
		}
	}
}

 

 

 

 

3 理解代理模式:

 认识代理模式:   客户端操作代理对象,代理操作真实对象, 代理对象夹在客户端和真实对象之间,相当于一个中转,我们可以再中转过程中,增加一些自定义功能,eg: 判断权限,只有权限足够下才可以调用真实对象,否则跳转登陆

 

代理对象和真实对象之间必须是一一对象的关系吗?

这种一一对应关系经常在虚代理下才有;

 

 

代理模式顺序图:



 

 

Java中的静态代理:(即自己手写接口实现类)

前面自己实现的代理模式,成为Java静态模式,特点就是 有共同接口,代理类和目标类实现这个接口,

缺点就是如果接口发生改变,那么代理类和目标都要变化不灵活。

 

Java中的动态代理:(交给Javase.reflect.invocationhandler来动态实现 取代手写方式)

依靠反射机制+动态生成类 = 生成动态代理对象 同样也是实现接口上的很多方法,

但是特点是:动态代理类只有一个方法invoke,即使接口方法变更时,动态代理也不需要变更(用一个高层抽象方法将变化给屏蔽掉)

 

Javaee动态代理使用案例: spring aop 面向切面编程

 

Java动态代理代码:

接口:
package cn.javass.dp.proxy.example5;

/**
 * 订单对象的接口定义
 */
public interface OrderApi {
	/**
	 * 获取订单订购的产品名称
	 * @return 订单订购的产品名称
	 */
	public String getProductName();
	/**
	 * 设置订单订购的产品名称
	 * @param productName 订单订购的产品名称
	 * @param user 操作人员
	 */
	public void setProductName(String productName,String user);
	/**
	 * 获取订单订购的数量
	 * @return 订单订购的数量
	 */
	public int getOrderNum();
	/**
	 * 设置订单订购的数量
	 * @param orderNum 订单订购的数量
	 * @param user 操作人员
	 */
	public void setOrderNum(int orderNum,String user);
	/**
	 * 获取创建订单的人员
	 * @return 创建订单的人员
	 */
	public String getOrderUser();
	/**
	 * 设置创建订单的人员
	 * @param orderUser 创建订单的人员
	 * @param user 操作人员
	 */
	public void setOrderUser(String orderUser,String user);
}

目标类:
package cn.javass.dp.proxy.example5;

/**
 * 订单对象
 */
public class Order implements OrderApi{
	/**
	 * 订单订购的产品名称
	 */
	private String productName;
	/**
	 * 订单订购的数量
	 */
	private int orderNum;
	/**
	 * 创建订单的人员
	 */
	private String orderUser;
	
	/**
	 * 构造方法,传入构建需要的数据
	 * @param productName 订单订购的产品名称
	 * @param orderNum 订单订购的数量
	 * @param orderUser 创建订单的人员
	 */
	public Order(String productName,int orderNum,String orderUser){
		this.productName = productName;
		this.orderNum = orderNum;
		this.orderUser = orderUser;
	}
	
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName,String user) {
		this.productName = productName;
	}
	public int getOrderNum() {
		return orderNum;
	}
	public void setOrderNum(int orderNum,String user) {
		System.out.println("调用到真正方法...");
		this.orderNum = orderNum;
	}
	public String getOrderUser() {
		return orderUser;
	}
	public void setOrderUser(String orderUser,String user) {
		this.orderUser = orderUser;
	}
	public String toString(){
		return "productName="+this.getProductName()+",orderNum="+this.getOrderNum()+",orderUser="+this.getOrderUser();
	}
}


动态代理类: 获取动态代理类对象的方法中 使用形参来接受真正目标对象,以实现目标对象和代理对象的绑定:

package cn.javass.dp.proxy.example5;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 使用Java中的动态代理
 */
public class DynamicProxy implements InvocationHandler{
	/**
	 * 被代理的对象
	 */
	private OrderApi order = null;
	/**
	 * 获取绑定好 代理和具体目标对象 后的目标对象的接口 (动态获取获取目标对象的代理对象)
	 * @param order 具体的订单对象,相当于具体目标对象
	 */
	public OrderApi getProxyInterface(Order order){
		this.order = order;
		//把真正的订单对象和动态代理关联起来, 即将创建目标对象的动态代理对象 交给 javase.reflect.invocationhandler来做, 取代你手动new出一个类 手写目标的对象的代理类(这叫Java静态代理)
		OrderApi orderApi = (OrderApi) Proxy.newProxyInstance(
				order.getClass().getClassLoader(),// 目标对象
				order.getClass().getInterfaces(), // 目标对象所有的接口
				this);
		return orderApi;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("调用目标对象的所有方法前,动态代理下,都会先走我...");
		//下面是针对目标对象不同方法添加逻辑判断
		
		//如果是调用setter方法就需要检查权限
		if(method.getName().startsWith("set")){
			//如果不是创建人,那就不能修改
			if(order.getOrderUser()!=null && order.getOrderUser().equals(args[1])){
				//可以操作
				return method.invoke(order, args);// 用着一句话 代替所有访问真实对象的方法
			}else{
				System.out.println("对不起,"+args[1]+",您无权修改本订单中的数据");
			}
		}else{
			//不是调用的setter方法就继续运行
			return method.invoke(order, args);
		}
		return null;
	}
}


客户端:
package cn.javass.dp.proxy.example5;

import java.lang.reflect.Proxy;

public class Client {
	public static void main(String[] args) {
		//张三先登录系统创建了一个订单
		Order order = new Order("设计模式",100,"张三");
		
		//创建一个动态代理
		DynamicProxy dynamicProxy = new DynamicProxy();		
		//然后把订单和动态代理关联起来
		OrderApi orderApi = dynamicProxy.getProxyInterface(order);
		
		orderApi.getOrderNum();
		
		//以下就需要使用被代理过的接口来操作了
		//李四想要来修改,那就会报错
		orderApi.setOrderNum(123, "李四");
		//输出order
		System.out.println("李四修改后订单记录没有变化:"+orderApi);
		//张三修改就不会有问题
		orderApi.setOrderNum(123, "张三");
		//再次输出order
		System.out.println("张三修改后,订单记录:"+orderApi);
		
	}
}

 

 

 

 

4 思考代理模式:

 本质: 控制对象的访问,能增强目标对象的功能,甚至替换目标对象的功能

何时使用:

a) 需要为一个对象在不同地址空间提供局部代表时,可以使用远程代理

b) 需要按照需要创建开销很大的对象时,可以使用虚代理(eg:加载小图标 大图片 查看用户基本资料 详细资料)

c) 需要控制对原始对象访问时,可以使用保护代理

d) 需要在访问对象的时候,执行一些附加操作时,可以使用智能指引代理

 

5代理模式脑图:

 



 

 

 

  • 大小: 36 KB
  • 大小: 95.7 KB
  • 大小: 23.6 KB
分享到:
评论

相关推荐

    Java设计模式——代理设计模式(静态代理和动态代理)

    Java设计模式——代理设计模式(静态代理和动态代理) 各种情况例子源码

    浅析Java设计模式【3】——代理.pdf

    浅析Java设计模式【3】——代理.pdf

    设计模式实现——代理模式

    http://blog.csdn.net/t1234xy4/article/details/52729467

    Python设计模式之代理模式实例详解

    设计模式——代理模式 代理模式(Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问 实现:戴励 替 卓贾易 送礼物给 娇娇 模型: #公共接口类 class InterFace: def request(self): pass #真实类 cla

    iOS设计模式——委托(delegate)例子

    代码实现代理模式。具体文章简介可以参考博文:http://blog.csdn.net/lovefqing/article/details/8270111

    设计模式可复用面向对象软件的基础.zip

    书名: 设计模式可复用面向对象软件的基础 英文原书名: Design Patterns:Elements of Reusable Object-Oriented software 作者: Erich Gamma 等 译者: 李英军 马晓星 蔡敏 刘建中 书号: 7-111-07575-7 页码: 254 定价...

    java 设计模式 静态代理模式

    //代理模式内部引用了真实角色 public void requst() { this.preRequest(); //在真实角色操作之前所附加的操作 if(null == realsubject) { realsubject = new ReallSubject(); } realsubject.requst(); // ...

    设计模式代码——c#

    C#设计模式(23种设计模式) 1. 单件模式(Singleton Pattern) 2. 抽象工厂(Abstract Factory) 3. 建造者模式(Builder) 4. 工厂方法模式(Factory Method) 5. 原型模式(Prototype) 结构型: 6. 适配器...

    Java设计模式 版本2

    设计模式之代理模式,请求的链式处理——职责链模式,请求发送者与接收者解耦——命令模式,自定义语言的实现——解释器模式,遍历聚合对象中的元素——迭代器模式,协调多个对象之间的交互——中介者模式,撤销功能...

    java设计模式

    目录: 前 言 第一部分 大旗不挥,谁敢冲锋——热身篇 第1章 单一职责原则 1.1 我是“牛”类,我可以担任多职吗 1.2 绝杀技,打破你的传统思维 1.3 我单纯,所以我快乐 1.4 最佳实践 ...附录:23个设计模式

    深入浅出设计模式(中文版)

    4.4.2现实中的装饰模式——相架 126 4.4.3C#实例——图书馆中的项目 127 4.4.4Java实例——自定义JButton 131 4.4.5优势和缺陷 133 4.4.6应用情景 134 4.5FacadePattern(外观模式) 134 4.5.1定义 134 4.5.2...

    深入浅出设计模式(中文版电子版)

    4.4.2现实中的装饰模式——相架 126 4.4.3C#实例——图书馆中的项目 127 4.4.4Java实例——自定义JButton 131 4.4.5优势和缺陷 133 4.4.6应用情景 134 4.5FacadePattern(外观模式) 134 4.5.1定义 134 4.5.2...

    java和设计模式ppt教程

    java和设计模式ppt包含工厂模式、建造模式、原始模型模式、单例模式、结构模式、适配器、桥梁模式、合成模式、装饰模式、门面模式、享元模式、代理模式、行为模式、解释器模式、迭代子模式、调停者模式、备忘录模式...

    管理系统javasal源码-Design-Patterns-Demo:超全的设计模式——理论+实现demo

    管理系统java sal源码 [toc] 设计模式 源码地址: 博客地址: 分类 序号 模式 & ...创建型模式:这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 ...Pattern)代理模式(Proxy Pattern)

    24种设计模式介绍与6大设计原则

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、...

    设计模式--可复用面向对象软件的基础

    1.2 Smalltalk MVC中的设计模式 1.3 描述设计模式 1.4 设计模式的编目 1.5 组织编目 1.6 设计模式怎样解决设计问题 1.7 怎样选择设计模式 1.8 怎样使用设计模式 第二章 实例研究:设计一个文档编辑器 2.1 设计问题...

    设计模式Demo

    结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、...

    C#23种设计模式_示例源代码及PDF

    代理模式 代理模式给某一个对象提供一个代理对象, 代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户 不想 或者不能够直接引用一个对象, 代理对象可以在客户和目标对象直接起到中介的...

Global site tag (gtag.js) - Google Analytics