Proxy模式(代理[延迟]模式)
Proxy??
Proxy是“代理人”的意思,它指的是代替别人进行工作的人。代理实际上就是使用委托的机制,在代理的过程中你可以做点其他的事情,然后再来执行被代理对象的代码。
- 知识储备
- 什么时候使用:
GoF书(请参见附录E[GoF])在讲解Proxy模式时,使用了一个可以在文本中嵌入图形对象(例如图片等)的文本编辑器作为例子。为了生成这些图形对象,需要读取图片文件,这很耗费时间。因此如果在打开文档时就生成有所的图形对象,就会导致文档打开时间过长。所以,最好是当用户浏览至文本中各个图形对象时,再去生成它们的实例。这时,Proxy模式就有了用武之地。
- 有那些代理:
- Virtual Proxy(虚拟代理)Virtual Proxy就是本章中学习的Proxy模式。只有当真正需要实例时,它才生成和初始化实例。
- Remote Proxy(远程代理)Remote Proxy可以让我们完全不必在意RealSubject角色是否在远程网络上,可以如同它在自己身边一样(透明性地)调用它的 方法。Java的RMI(RemoteMethodInvocation:远程方法调用)就相当于Remote Proxy。
-
Access Proxy Access Proxy 用于在调用RealSubject角色的功能时设置访问限制。例如,这种代理可以只允许指定的用户调用方法,而当其他用户调用方法时则报错。
- 静态代理
1.使用委托机制代理人只代理他能解决的问题。当遇到他不能解决的问题时,还是会“转交”给本人去解决。
这里的“转交”就是在本书中多次提到过的“委托”。从PrinterProxy类的print方法中调用real.print方法正是这种“委托”的体现。
- 为了实现PrinterProxy类可以从printer类中分离出来作为独立的组件使用,而且只要是实现了Printable接口的类都可以扮演Proxy的角色。需要使用反射实例
···
private synchronized void realize(String classname) {
if(real==null){
try {
real = ((Printable) Class.forName(classname).newInstance());
}catch (ClassNotFoundException e){
System.out.println(“没有发现”+classname);
}catch (Exception e){
e.printStackTrace();
}
}
}
···
- 动态代理
在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的。首先我们先来看看java的API帮助文档是怎么样对这两个类进行描述的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。我们来看看InvocationHandler这个接口的唯一一个方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我们看到这个方法一共接受三个参数,那么这三个参数分别代表什么呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
理清职责
- 实现一个带名字的打印机
名字=====》》说明
Printer || 表示带名字的打印机的类(本人)
Printable || Printer和PrinterProxy的共同接口
PrinterProxy || 表示带名字的打印机的类(代理人)
Main ||测试程序行为的类
UML
类图:
时序图:
Code
- Printable
public interface Printable {
// 设置打印名字
void setPrinterName(String name);
// 获取打印名字
String getPrinterName();
// 显示文字
void print(String string);
}
- Printer
···
public class Printer implements Printable{
private String name;
public Printer() {
heavyjob("正在生成Printer实例");
}
public Printer(String name) {
this.name = name;
heavyjob("正在生成Printer实例("+name+")");
}
/**
* 模拟一个高负载任务
* @param string
*/
private void heavyjob(String string) {
System.out.println(string);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.print(".");
}
}
@Override
public void setPrinterName(String name) {
this.name=name;
}
@Override
public String getPrinterName() {
return name;
}
@Override
public void print(String string) {
System.out.println("==="+name+"===");
System.out.println(string);
}
}
···
- PrinterProxy
public class PrinterProxy implements Printable{
private String name;
private Printer real;
/**
* 不论 setPrinterName 方法和getPrinterName 方法被调用多少次,
* 都不会生成Printer类的实例。只有当真正需要本人时,
* 才会生成printer类的实例(printerProxy类的调用者完全不知道是否生成了本人,也不用在意是否生成了本人)。
* @param name
*/
public PrinterProxy(String name) {
this.name = name;
}
@Override
public synchronized void setPrinterName(String name) {
if(real!=null){
real.setPrinterName(name);
}
this.name=name;
}
@Override
public String getPrinterName() {
return name;
}
@Override
public void print(String string) {
realize();
real.print(string);
}
private synchronized void realize() {
if(real==null) real=new Printer(name);
}
}
public class MainT {
public static void main(String[] args) {
Printable p=new PrinterProxy("Tom");
System.out.println("现在是"+p.getPrinterName());
p.setPrinterName("Cat");
System.out.println("现在是"+p.getPrinterName());
p.print("我是 Tomcat");
}
}
- 结果:
现在是Tom
现在是Cat
正在生成Printer实例(Cat)
.....
===Cat===
我是 Tomcat