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代理模式脑图:
相关推荐
基于Java的设计模式——代理模式demo的实现(高分课设)个人经导师指导并认可通过的98分大作业设计项目,适用人群:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业或毕业设计,作为“参考资料”使用...
在Java编程中,设计模式是一种解决常见问题的模板或最佳实践,它可以帮助开发者编写更加灵活、可维护和可扩展的代码。代理设计模式是其中的一种,它的主要作用是在...在实际开发中,应根据项目需求选择合适的代理模式。
**设计模式实现——代理模式** 在软件工程中,设计模式是一种通用可重用的解决方案,它描述了在特定上下文中经常出现的问题以及该问题的解决方案。代理模式是设计模式的一种,它提供了一种对目标对象的间接访问方式...
6. **代理模式**:为其他对象提供一种代理以控制对该对象的访问。代理可以在目标对象前增加额外的功能,如缓存、日志记录、事务控制等。 7. **建造者模式**:将一个复杂对象的构建与其表示分离,使得同样的构建过程...
《设计模式——GFour》是一本深受IT从业者喜爱的经典书籍,尤其在C++开发者中具有广泛影响力。设计模式是软件工程领域中的一种最佳实践,它总结了在特定情境下解决问题的常见方法,使得代码可读性更强,复用性更高,...
**Java设计模式——代理模式详解** 代理模式是软件设计模式中的一个重要组成部分,它在Java编程中扮演着举足轻重的角色。代理模式的核心思想是为一个对象提供一个替身,这个替身即代理对象,代理对象可以控制对原...
代理模式为其他对象提供一种代理以控制对这个对象的访问;组合模式将对象组合成树形结构以表示“部分-整体”的层次结构。 3. **行为型模式**:这类模式涉及对象之间的责任分配。命令模式将请求封装为一个对象,从而...
装饰模式是一种设计模式...通过阅读和理解《设计模式:可复用面向对象软件的基础》等经典书籍,以及像博客“设计模式——装饰模式”这样的在线资源,我们可以深入掌握并灵活运用装饰模式,提高代码的可维护性和扩展性。
《设计模式——Java语言中的应用》是一本专为Java开发者深入理解面向对象设计而编写的经典书籍。设计模式是软件工程领域中经过实践验证的、解决常见问题的有效方案,它们代表了在特定上下文中,针对特定问题的最优...
### 浅析Java设计模式【3】——代理 #### 一、代理模式概述 代理模式是一种行为型设计模式,主要用于在客户端与目标对象之间起到一个中介的作用,通过代理对象来控制对目标对象的访问。代理模式的核心在于它可以...
在《设计模式——可复用面向对象软件的基础》这本书中,作者Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides(合称GoF)首次提出了23种面向对象设计模式。这些模式涵盖了不同设计场景,如创建型模式、...
在iOS开发中,设计模式是实现高效、可维护和可扩展代码的关键元素。本文将重点关注“委托”(Delegate)设计模式,这是一种常见的模式,用于在对象之间建立通信和协调行为。通过实现委托,一个对象(委托者)可以...
"设计模式之美——教你写出高质量代码"这个主题旨在帮助开发者更好地理解和应用设计模式,从而提升代码的质量和可维护性。设计模式不仅对面试有所帮助,也是职场发展中的必备技能,无论你使用哪种开发语言。 设计...
本文将深入探讨C#中的一个关键设计模式——代理模式,它属于结构型模式的一种,适用于管理和控制对象的访问。我们将从概念、分类、实现方式以及实际应用等方面进行详细的讲解。 1. **代理模式概念**: 代理模式是一...
### JAVA设计模式总结之23种设计模式及六大原则 #### 一、设计模式之六大原则 ##### 总原则:开闭原则(Open Close Principle) 开闭原则是设计模式中最核心的原则之一,它强调的是软件实体(类、模块、函数等)...