代理模式:

为另一个对象提供一个替身或占位符以控制对这个对象的访问。
被代理的对象可以是远程的对象、创建开销大的对象或需要安全控制的对象。
 
一,远程代理:
客户对象所做的就像是在做远程方法调用,但其实只是调用本地堆中的“代理”对象上的方法,再由代理处理所有网络通信的低层细节。
 
远程代理就好比“远程对象的本地代表”。
“远程对象”是指活在不同的Java虚拟机堆的对象(或在不同的地址空间运行的远程对象)。
“本地代表”是指可以由本地方法调用的对象,其行为会转发到远程对象中。
 
Java RMI已内置了远程代理服务。
 
步骤:
1,制作远程接囗;
 
  1. public interface GumballMachineRemote extends Remote {//必须扩展java.rmi.Remote 
  2.     public int getCount() throws RemoteException;//准备支持的方法必须抛出RemoteException 
  3.     public String getLocation() throws RemoteException; 
  4.     public State getState() throws RemoteException; 
  5.     //所有返回类型必须是原语类型或可序列化类型 
 
 
  1. public interface State extends Serializable {//扩展Serializable接囗,实现序列化,这样所有子类中的State就可以在网络上传送了 
  2.     public void insertQuarter(); 
  3.     public void ejectQuarter(); 
  4.     public void turnCrank(); 
  5.     public void dispense(); 
//状态对象维护着一个对糖果机的引用,我们不希望整个糖果机都被序列化并随State对象一起传送,所以子类做如下修正
 
  1. public class NoQuarterState implements State { 
  2.     transient GumballMachine gumballMachine;//对State的每个实现,我们都在GumballMachine实例变量前加上transient关键字,告诉JVM不要序列化这个字段 
  3.   
  4.     //其它方法 
2,制作远程的实现;
//GumballMachine要继承UnicastRemoteObject,以成为一个远程服务。另外需要实现远程接囗GumballMachineRemote
 
  1. public class GumballMachine 
  2.         extends UnicastRemoteObject implements GumballMachineRemote  
  3.     //这里有实例变量 
  4.  
  5.     public GumballMachine(String location, int numberGumballs) throws RemoteException {//构造器需要抛出RemoteException,因为超类是这么做的 
  6.         //这里代码 
  7.     } 
  8.   
  9.     public int getCount() { 
  10.         return count; 
  11.     } 
  12.   
  13.     public State getState() { 
  14.         return state; 
  15.     } 
  16.   
  17.     public String getLocation() { 
  18.         return location; 
  19.     } 
  20.     //其他方法 
3,在RMI registry中进行注册:执行rmiregistry
4,启动服务
 
二,虚拟代理:
虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象时才创建它,在对象创建前和创建中时,由虚拟代理来扮演对象的替身,在对象创建后,代理就会将请求直接委托给对象。
 
以显示CD封面为例:
代理对象ImageProxy与被代理对象ImageIcon都实现Icon接囗;
ImageProxy首先创建一个ImageIcon,然后从网络加载图像;
在加载未完成时,ImageProxy显示“CD封面加载中,请稍候.....”
当加完完成后,ImageProxy把所有方法调用委托给真正的ImageIcon;
如果用户请求新的图像,我们就创建新代理,重复这样的过程。
 
三,保护代理:
所谓“保护代理”就是根据访问权限决定客户可否访问对象的代理。
 
Java在java.lang.reflect包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多外接囗,并将方法的调用转发到你所指定的类,因为实际的代理类是在运行时创建的,我们称这个Java技术为:动态代理。
本书利用Java的动态代理创建保护代理实现。
 
以书中配对服务应用为例:
首先设计要求顾客不能改变自己的评分,也不能改变其他顾客的个人信息;由此可知这里需要创建两个代理:一个访问自己的PersonBean对象,一个访问另一顾客的PersonBean对象,这样代理就可以控制在那种情况下允许那种请求;
 
实例步骤:
1,创建两个InvocationHandler,其中一个给拥有者使用,另一个给非拥有者使用。
什么是InvocationHandler呢?你可以这么想,当代理的方法被调用时,代理就会把这个调用转发给InvocationHandler,但是这并不是通过调用InvocationHandler的相应方法做到的,那么是如何做到的呢?
InvocationHandler只有一个名为invoke的方法,不管代理被调用的是何种方法,各理器被调用的一定是invoke()方法,那么invoke是如何工作的呢?
 
例设proxy的setHotOrNotRating方法被调用:
ownerProxy.setHotOrNotRating(10);
 
proxy会接着调用InvocationHandler的invoke()方法:
invoke(代理对象实例ownerProxy,调用方法setHotOrNotRating,参数数组{10});
 
 
  1. package headfirst.proxy.javaproxy; 
  2.   
  3. import java.lang.reflect.*; 
  4.  
  5. public class OwnerInvocationHandler implements InvocationHandler {  
  6.     PersonBean person; 
  7.   
  8.     public OwnerInvocationHandler(PersonBean person) { 
  9.         this.person = person;//被调用对象 
  10.     } 
  11.   
  12.     public Object invoke(Object proxy, Method method, Object[] args)  
  13.             throws IllegalAccessException { 
  14.    
  15.         try { 
  16.             if (method.getName().startsWith("get")) { 
  17.                 return method.invoke(person, args);//允许操作 
  18.             } else if (method.getName().equals("setHotOrNotRating")) { 
  19.                 throw new IllegalAccessException();//禁止操作,抛出运行时异常 
  20.             } else if (method.getName().startsWith("set")) { 
  21.                 return method.invoke(person, args); 
  22.             }  
  23.         } catch (InvocationTargetException e) { 
  24.             e.printStackTrace(); 
  25.         }  
  26.         return null; 
  27.     } 
  28.  
  29. package headfirst.proxy.javaproxy; 
  30.   
  31. import java.lang.reflect.*; 
  32.   
  33. public class NonOwnerInvocationHandler implements InvocationHandler {  
  34.     PersonBean person; 
  35.   
  36.     public NonOwnerInvocationHandler(PersonBean person) { 
  37.         this.person = person; 
  38.     } 
  39.   
  40.     public Object invoke(Object proxy, Method method, Object[] args)  
  41.             throws IllegalAccessException { 
  42.    
  43.         try { 
  44.             if (method.getName().startsWith("get")) { 
  45.                 return method.invoke(person, args); 
  46.             } else if (method.getName().equals("setHotOrNotRating")) { 
  47.                 return method.invoke(person, args); 
  48.             } else if (method.getName().startsWith("set")) { 
  49.                 throw new IllegalAccessException(); 
  50.             }  
  51.         } catch (InvocationTargetException e) { 
  52.             e.printStackTrace(); 
  53.         }  
  54.         return null; 
  55.     } 
 
2,创建Proxy类并实例化Proxy对象
 
 
  1. PersonBean getOwnerProxy(PersonBean person) { 
  2.          
  3.        return (PersonBean) Proxy.newProxyInstance(  
  4.             person.getClass().getClassLoader(), 
  5.             person.getClass().getInterfaces(), 
  6.                new OwnerInvocationHandler(person)); 
  7.  
  8. PersonBean getNonOwnerProxy(PersonBean person) { 
  9.      
  10.        return (PersonBean) Proxy.newProxyInstance( 
  11.             person.getClass().getClassLoader(), 
  12.             person.getClass().getInterfaces(), 
  13.                new NonOwnerInvocationHandler(person)); 
 
3,测试程序:
 
  1. package headfirst.proxy.javaproxy; 
  2.  
  3. import java.lang.reflect.*; 
  4. import java.util.*; 
  5.  
  6. public class MatchMakingTestDrive { 
  7.     Hashtable datingDB = new Hashtable(); 
  8.      
  9.     public static void main(String[] args) { 
  10.         MatchMakingTestDrive test = new MatchMakingTestDrive(); 
  11.         test.drive(); 
  12.     } 
  13.   
  14.     public MatchMakingTestDrive() { 
  15.         initializeDatabase(); 
  16.     } 
  17.  
  18.     public void drive() { 
  19.         PersonBean joe = getPersonFromDatabase("Joe Javabean");  
  20.         PersonBean ownerProxy = getOwnerProxy(joe); 
  21.         System.out.println("Name is " + ownerProxy.getName()); 
  22.         ownerProxy.setInterests("bowling, Go"); 
  23.         System.out.println("Interests set from owner proxy"); 
  24.         try { 
  25.             ownerProxy.setHotOrNotRating(10); 
  26.         } catch (Exception e) { 
  27.             System.out.println("Can't set rating from owner proxy"); 
  28.         } 
  29.         System.out.println("Rating is " + ownerProxy.getHotOrNotRating()); 
  30.  
  31.         PersonBean nonOwnerProxy = getNonOwnerProxy(joe); 
  32.         System.out.println("Name is " + nonOwnerProxy.getName()); 
  33.         try { 
  34.             nonOwnerProxy.setInterests("bowling, Go"); 
  35.         } catch (Exception e) { 
  36.             System.out.println("Can't set interests from non owner proxy"); 
  37.         } 
  38.         nonOwnerProxy.setHotOrNotRating(3); 
  39.         System.out.println("Rating set from non owner proxy"); 
  40.         System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating()); 
  41.     } 
  42.  
  43.     PersonBean getOwnerProxy(PersonBean person) { 
  44.          
  45.         return (PersonBean) Proxy.newProxyInstance(  
  46.                 person.getClass().getClassLoader(), 
  47.                 person.getClass().getInterfaces(), 
  48.                 new OwnerInvocationHandler(person)); 
  49.     } 
  50.  
  51.     PersonBean getNonOwnerProxy(PersonBean person) { 
  52.          
  53.         return (PersonBean) Proxy.newProxyInstance( 
  54.                 person.getClass().getClassLoader(), 
  55.                 person.getClass().getInterfaces(), 
  56.                 new NonOwnerInvocationHandler(person)); 
  57.     } 
  58.  
  59.     PersonBean getPersonFromDatabase(String name) { 
  60.         return (PersonBean)datingDB.get(name); 
  61.     } 
  62.  
  63.     void initializeDatabase() { 
  64.         PersonBean joe = new PersonBeanImpl(); 
  65.         joe.setName("Joe Javabean"); 
  66.         joe.setInterests("cars, computers, music"); 
  67.         joe.setHotOrNotRating(7); 
  68.         datingDB.put(joe.getName(), joe); 
  69.  
  70.         PersonBean kelly = new PersonBeanImpl(); 
  71.         kelly.setName("Kelly Klosure"); 
  72.         kelly.setInterests("ebay, movies, music"); 
  73.         kelly.setHotOrNotRating(6); 
  74.         datingDB.put(kelly.getName(), kelly); 
  75.     } 
 
4,其他代码:
 
  1. package headfirst.proxy.javaproxy; 
  2.  
  3. public interface PersonBean { 
  4.   
  5.     String getName(); 
  6.     String getGender(); 
  7.     String getInterests(); 
  8.     int getHotOrNotRating(); 
  9.   
  10.     void setName(String name); 
  11.     void setGender(String gender); 
  12.     void setInterests(String interests); 
  13.     void setHotOrNotRating(int rating);  
  14.   
  15.  
  16. package headfirst.proxy.javaproxy; 
  17.  
  18. public class PersonBeanImpl implements PersonBean { 
  19.     String name; 
  20.     String gender; 
  21.     String interests; 
  22.     int rating; 
  23.     int ratingCount = 0
  24.    
  25.     public String getName() { 
  26.         return name;     
  27.     }  
  28.    
  29.     public String getGender() { 
  30.         return gender; 
  31.     } 
  32.    
  33.     public String getInterests() { 
  34.         return interests; 
  35.     } 
  36.     
  37.     public int getHotOrNotRating() { 
  38.         if (ratingCount == 0) return 0; 
  39.         return (rating/ratingCount); 
  40.     } 
  41.    
  42.   
  43.     public void setName(String name) { 
  44.          = name; 
  45.     } 
  46.   
  47.     public void setGender(String gender) { 
  48.         this.gender = gender; 
  49.     }  
  50.    
  51.     public void setInterests(String interests) { 
  52.         this.interests = interests; 
  53.     }  
  54.    
  55.     public void setHotOrNotRating(int rating) { 
  56.         this.rating += rating;   
  57.         ratingCount++; 
  58.     } 
 
要点:
代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理访问的方式有很多种;
远程代理管理客户和远程对象之间的交互;
虚拟代理控制访问实例化开销大的对象;
保护代理基于调用者控制对对象方法的访问;
代理模式有很多变体;
代理在结构上类似装饰者,但是目的不同:装饰者模式为对象加上行为,而代理则是控制访问;
就象其他的包装者(wraapper)一样,代理会造成你的设计中的类的数目增加。