Proxy(代理)模式为对象提供一个代理或者占位来控制对该对象的访问。
图像代理
使用Proxy模式的设计有时非常脆弱,它们依赖转发方法来调用其底层对象。转发可能会建立一个非常脆弱并且需要经常维护的设计。
load()方法以JFrame对象为参数,用于在指定图像加载完毕之后进行回调。在执行load()方法的时候,它先以LOADING引用的图像对象为参数调用setImage(),然后重新绘制图形显示窗口,最后为自己启动一个单独的线程。而run()方法是在单独的线程中执行的,该方法根据构造器中指定的图像文件名创建一个新的ImageIcon对象,然后以该图像对象为参数调用setImage()方法,最后重绘该窗口。
远程代理
在Java的远程方法调用(RMI)在,代理对象用于将调用请求转发给在另一台计算机上运行的指定对象,客户可以很容易地获得这种代理对象。
若要使用RMI,首先要创建一个远程接口,定义需要在计算机之间交换的消息,再创建一个远程对象;该远程对象可实现远程接口,并拓展UnicastRemoteObject类
public interface Rocket extends Remote { void boost(double factor) throws RemoteException; double getApogee() throws RemoteException; double getPrice() throws RemoteException; }
public class RocketImpl extends UnicastRemoteObject implements Rocket { protected double price; protected double apogee; public RocketImpl(double price,double apogee) throws RemoteException{ this.price=price; this.apogee=apogee; } public void boost(double factor){ apogee *= factor; } public double getApogee(){ return apogee; } public double getPrice(){ return price; } }
RocketImpl对象作为服务提供者类运行在服务器上,客户端可以通过运行在本地的代理对象来访问RocketImpl对象的方法。
RocketImpl的实例运行在一台机器上,我们要让它能够被运行在其他机器上的Java程序访问,就必须在客户端为RocketImpl对象提供一个代理对象。为了实现此目的,客户端需要一个RocketImpl对象的代理。该代理类必须实现Rocket接口,并且提供用于与远程对象通信的附加特性。RMI的最大便利之一就是它能够自动创建这个代理类。为了自动生成代理类,我们必须把RocketImpl.java文件和Rocket.java接口文件存放在RMI注册程序的运行目录下。
在对象能够被远程访问之前,我们必须用运行在服务器上的RMI注册程序注册该对象。在服务器上运行了注册程序之后,我们便可以开始创建和注册RocketImpl对象。
public class RegisterRocket { public static void main(String[] args){ try{ Naming.rebind("rmi://localhost:5000/Biggie",biggie); System.out.println("Registered biggie"); }catch(Exception e){ e.printStackTrace(); } } }
运行RegisterRocket应用程序便可在服务器上创建并运行一个RocketImpl对象biggie。客户端只要能够访问Rocket接口和RocketImpl_stub类,便可以访问远程运行的biggie对象。如果只有一台机器,我们仍然可以测试这个RMI应用程序,不过要在localhost而不是另外一台主机上运行服务器端代码。
public class ShowRocketClient { public static void main(String[] args){ try{ Object obj=Naming.lookup("rmi://localhost:5000/Biggie"); Rocket biggie = (Rocket) obj; System.out.println("Apogee is " + biggie.getApogee()); }catch(Exception e){ System.out.println("Exception while looking up a rocket:"); e.printStackTrace(); } } }
RMI的优点在于它使得客户端程序只需与本地代理对象进行交互便可达到与远程对象通信的目的。RMI用户定义了客户端和
服务器端共享对象的接口。RMI为客户端和服务器端分别提供一个Rocket接口的实现类;这两个实现类相互协作,从而可
完成进程间的无缝通信。服务器端和客户端则不必关心这些细节。
动态代理
Java可支持动态代理的特性。借助于动态代理,可以使用代理对象来包装其他对象,使用代理对象截获对被包装对象的调
用请求,然后代理继续把这些请求转发给被包装对象。在执行被截获的调用之前或之后,我们可以编写相关的加强代码。
对动态代理加以限制可以防止包装其他随意的对象。在正常条件下,动态代理使你能够完全控制被包装对象的操作。
动态代理需要使用对象的类所实现的接口。代理对象可以截获的调用就是这些接口定义的调用。如果某个类可以实现要截
获的接口,就可以使用动态代理来包装那个类的实例。
public class ShowDynamicProxy { public static void main(String[] args){ Set s = new HashSet(); s = ImpatientProxy.newInstance(s); s.add(new BadApple("Lemon")); ... System.out.println("The set contains " + s.size() + " things."); } }
待续