读《研磨设计模式》-代码笔记-代理模式-Proxy

声明:
本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/




import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/*
 * 下面的代码关注两种代理:
 * 1.虚代理
 * 2.保护代理
 */

/*
 * 订单类接口
 * id 订单id
 * name 订单名称
 */
interface IOrder {
	
	void setName(String name);
	
	void setID(String id);
	
	String getName();
	
	String getID();
	
}

class Order implements IOrder {
	
	private String id;
	
	private String name;
	
	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public String getID() {
		return this.id;
	}

	public void setID(String id) {
		this.id = id;
	}
	
}


class OrderProxy implements IOrder {

	private Order order;
	
	private boolean nameLoaded;
	
	public OrderProxy(Order order) {
		this.order = order;
		this.nameLoaded = false;
	}
	
	/*
	 * 1、虚代理
	 * a. 客户查询订单信息时,只查询客户要求的信息:订单ID(假设这个查询比较快)
	 * b. 只有当用户要求更详细的信息(假设只是一个订单名称)时,才再次到数据库查询
	 * 这样做的原因是,通常查询“详细信息”时,会花费更多的时间和空间,有点“延迟加载”的感觉
	 */
	private void loadName() {
		if (!nameLoaded) {
			System.out.println("request name but no name, fetch it from database:");
			String name = "aName";		//模拟数据库操作
			order.setName(name);
			this.nameLoaded = true;
		}
	}

	public String getID() {
		return order.getID();
	}

	/*
	 * 2.保护代理,可以在这里加上条件,符合则调用真实的order的方法。例如只有订单创建者能修改订单名称
	 */
	public void setName(String name) {
		boolean canChangeName = true;		//根据实际设置,例如canChangeName = (name.equals(order.getCreateUser()));
		if (canChangeName) {
			order.setName(name);
			System.out.println("Request accepted. Name changed.");
		}
	}

	public void setID(String id) {
		order.setID(id);
	}

	public String getName() {
		if (!nameLoaded) {
			this.loadName();
		}
		return this.order.getName();
	}
	
}


public class ProxyPattern {

	public static void main(String[] args) {
		
		//1.静态代理
		ProxyPattern p = new ProxyPattern();
		IOrder order = p.getOrderFromDatabase();      //模拟从数据库查询Order
		System.out.println("id:" + order.getID());
		
		//测试虚代理。如果“客户”没有调用getName,那“到数据库查询name”这个操作就可以不执行了
		System.out.println("name:" + order.getName());
		
		//测试保护代理
		order.setName("newName");
		System.out.println(order.getName());
		
		//2.动态代理
		//准备测试数据
		Order order2 = new Order();
		order2.setID("originalID");
		order2.setName("originalName");
		DynamicProxy dProxy = new DynamicProxy();
		IOrder iOrder = dProxy.getProxyOrder(order2);
		iOrder.setID("anotherID");
		iOrder.setName("anotherName");
		System.out.println(iOrder.getID() + ","+ iOrder.getName());
	}

	//模拟从数据库查询Order
	public IOrder getOrderFromDatabase() {
		//这里创建的是代理,而非真实的Order
		//IOrder order = new Order();
		OrderProxy proxy = new OrderProxy(new Order());
		System.out.println("simple info. Only fetch order's id from database:");
		String id = "aID";		//模拟从数据库取得ID
		proxy.setID(id);
		return proxy;
	}
}


//=======================以上实现代理的方式称为静态代理===========================================

/**
 * JDK里面的InvocationHandler来实现代理,称为动态代理
 * 静态代理实现的时候,在Subject接口(IOrder)上定义很多的方法,代理类里面自然也要实现很多方法;
 * 而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。
 * 这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。
 * Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。
 */
class DynamicProxy implements InvocationHandler{

	private IOrder order;
	
	public IOrder getProxyOrder(Order order) {
		this.order = order;
	    //把真正的订单对象和动态代理关联起来
		IOrder iOrder = (IOrder) Proxy.newProxyInstance(order.getClass().getClassLoader(),
											              order.getClass().getInterfaces(),
											              this);
       return iOrder;
	}
	
	/*
	 *  这个方法并不需要我们显式的调用
	 *  我们在getProxyOrder后获得一个IOrder,当IOrder调用某方法时,会触发invoke()这个方法
	 *  也就是所有方法调用都被invoke()拦截了
	 *  这里面的第一个参数obj是什么呢?看API的说法是“proxy instance”。一般不用到这一个参数
	 *  passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, 
	 *  and an array of type Object containing the arguments.  
	 */
	public Object invoke(Object obj, Method method, Object[] aobj) throws Throwable {
		Object result = null;
		
		//假设不允许执行setID操作
		if (method.getName().equalsIgnoreCase("setID")) {
			System.out.println(method.getName() + ":Access denied.");
		} else {
			//result = method.invoke(obj, aobj);		//不能这样写
			result = method.invoke(order, aobj);        //调用真实的order的method
		}
		//System.out.println(String.format("Invoke '%s' ; result = %s", method.getName(), result));
		return result;
	}
	
}



你可能感兴趣的:(java,设计模式)