一. 定义
为其他对象提供一种代理以控制对这个对象的访问。
二. 结构和说明
Subject:目标接口,定义代理和具体目标对象的接口
public interface Subject { // 示意方法:一个抽象的请求方法 public void request(); }RealSubject:具体的目标对象,真正实现目标接口要求的功能
public class RealSubject implements Subject{ public void request() { //执行具体的功能处理 } }Pxory:代理对象,通常具有如下功能:
1. 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象
2.保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象
3.可以控制对具体目标对象的访问
public class Proxy implements Subject { // 持有被代理的具体的目标对象 private RealSubject realSubject = null; /** * 构造方法,传入被代理的具体的目标对象 * @param realSubject 被代理的具体的目标对象 */ public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } public void request() { // 在转调具体的目标对象前,可以执行一些功能处理 // 转调具体的目标对象的方法 realSubject.request(); // 在转调具体的目标对象后,可以执行一些功能处理 } }
三. 示例:静态代理
现在有一个订单系统,要求是:一旦订单被创建,只有订单的创建人才能修改订单中的数据,其他人不能修改。
相当于现在如果有了一个订单对象实例,那么就需要控制外部对它的访问,满足条件的可以访问,而不满足条件的就不能访问了。
1. 订单对象的接口:
public interface OrderApi { // 获取订单订购的产品名称 public String getProductName(); // 设置订单订购的产品名称 public void setProductName(String productName, String user); // 获取订单订购的数量 public int getOrderNum(); // 设置订单订购的数量 public void setOrderNum(int orderNum, String user); // 获取创建订单的人员 public String getOrderUser(); // 设置创建订单的人员 public void setOrderUser(String orderUser, String user); }2. 订单对象
public class Order implements OrderApi { // 订单订购的产品名称 private String productName; // 订单订购的数量 private int orderNum; // 创建订单的人员 private String orderUser; /** * 构造方法,传入构建需要的数据 */ public Order(String productName, int orderNum, String orderUser) { this.productName = productName; this.orderNum = orderNum; this.orderUser = orderUser; } // getter and setter }3. 订单的代理对象
public class OrderProxy implements OrderApi { // 持有被代理的具体的目标对象 private Order order = null; /** * 构造方法,传入被代理的具体的目标对象 * @param realSubject 被代理的具体的目标对象 */ public OrderProxy(Order realSubject) { this.order = realSubject; } public void setProductName(String productName, String user) { // 控制访问权限,只有创建订单的人员才能够修改 if (user != null && user.equals(this.getOrderUser())) { order.setProductName(productName, user); } else { System.out.println("对不起" + user + ",您无权修改订单中的产品名称。"); } } public void setOrderNum(int orderNum, String user) { // 控制访问权限,只有创建订单的人员才能够修改 if (user != null && user.equals(this.getOrderUser())) { order.setOrderNum(orderNum, user); } else { System.out.println("对不起" + user + ",您无权修改订单中的订购数量。"); } } public void setOrderUser(String orderUser, String user) { // 控制访问权限,只有创建订单的人员才能够修改 if (user != null && user.equals(this.getOrderUser())) { order.setOrderUser(orderUser, user); } else { System.out.println("对不起" + user + ",您无权修改订单中的订购人。"); } } public int getOrderNum() { return this.order.getOrderNum(); } public String getOrderUser() { return this.order.getOrderUser(); } public String getProductName() { return this.order.getProductName(); } public String toString() { return "productName: " + this.getProductName() + ", orderNum: " + this.getOrderNum() + ",orderUser: " + this.getOrderUser(); } }4. 客户端
public class Client { public static void main(String[] args) { // 张三先登录系统创建了一个订单 OrderApi order = new OrderProxy(new Order("设计模式", 100, "张三")); // 李四想要来修改,那就会报错 order.setOrderNum(123, "李四"); // 输出order System.out.println("李四修改后订单记录没有变化:" + order); // 张三修改就不会有问题 order.setOrderNum(123, "张三"); // 再次输出order System.out.println("张三修改后,订单记录:" + order); } }对不起李四,您无权修改订单中的订购数量。
上面的实现就是java的静态代理,这种实现方式有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活。
四. 示例:动态代理
public class DynamicProxy implements InvocationHandler { // 被代理的对象 private OrderApi order = null; /** * 获取绑定好代理和具体目标对象后的目标对象的接口 * @param order 具体的订单对象,相当于具体目标对象 * @return 绑定好代理和具体目标对象后的目标对象的接口 */ public OrderApi getProxyInterface(Order order) { // 设置被代理的对象,好方便invoke里面的操作 this.order = order; // 把真正的订单对象和动态代理关联起来 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 { // 如果是调用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; } }动态代理和静态代理相比,明显的变化是:虽然Subject接口定义了很多方法,但是动态代理类始终只有一个invoke方法(代理类是动态生成的),这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了
五. 何时选用代理模式
1. 如果需要控制对原始对象的访问的时候,如权限控制
2. 需要在访问对象的时候执行一些附加操作的时候,例如打印日志等