Proxy 模式
Proxy是“代理人”的意思,它指的是代替别人进行工作的人。当不一定需要本人亲自进行工作时,就可以寻找代理人去完成工作。但代理人毕竟只是代理人,能代替本人做的事情终究是有限的。因此,当代理人遇到无法自己解决的事情时就会去找本人解决该问题。
在面向对象编程中,“本人”和“代理人”都是对象。如果“本人”对象太忙了,有些工作无法自己亲自完成,就将其交给“代理人”对象负责。
示例程序
类和接口的一览表
名字 | 说明 |
---|---|
Printer | 表示带名字的打印机的类(本人) |
Printable | Printer和PrinterProxy的共同接口 |
PrinterProxy | 表示带名字的打印机的类(代理人) |
Main | 测试程序行为的类 |
示例程序的类图
示例程序的时序图
Printer
public class Printer implements Printable {
private String name;
public Printer() {
heavyJob("正在生成Printer的实例");
}
public Printer(String name) { // 构造函数
this.name = name;
heavyJob("正在生成Printer的实例(" + name + ")");
}
@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);
}
private void heavyJob(String msg) { // 重活
System.out.print(msg);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.print(".");
}
System.out.println("结束。");
}
}
Printable
public interface Printable {
/**
* 设置名字
* @param name
*/
void setPrinterName(String name);
/**
* 获取名字
* @return
*/
String getPrinterName();
/**
* 显示文字(打印输出)
* @param string
*/
void print(String string);
}
PrinterProxy
public class PrinterProxy implements Printable {
// 名字
private String name;
// “本人”
private Printer real;
public PrinterProxy() {
}
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);
}
}
}
Main
public class Main {
public static void main(String[] args) {
Printable p = new PrinterProxy("Alice");
System.out.println("现在的名字是" + p.getPrinterName() + "。");
p.setPrinterName("Bob");
System.out.println("现在的名字是" + p.getPrinterName() + "。");
p.print("Hello, world.");
}
}
Proxy 模式
1.Subject(主体)
Subject角色定义了使Proxy角色和RealSubject角色之间具有一致性的接口。由于存在Subject角色,所以Client角色不必在意它所使用的究竟是Proxy角色还是RealSubject角色。在示例程序中,由Printable接口扮演此角色。
2.Proxy(代理人)
Proxy角色会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject角色。Proxy角色实现了在Subject角色中定义的接口(API)。在示例程序中,由PrinterProxy类扮演此角色。
3.RealSubject(实际的主体)
“本人”RealSubject角色会在“代理人”Proxy角色无法胜任工作时出场。它与Proxy角色一样,也实现了在Subject角色中定义的接口(API)。在示例程序中,由Printer类扮演此角色。
4.Client(请求者)
使用Proxy模式的角色。
Proxy 模式通用类图
代理与委托
代理人只代理他能解决的问题。当遇到他不能解决的问题时,还是会“转交”给本人去解决。这里的“转交”就是在本书中多次提到过的“委托”。
HTTP代理
提到代理,许多人应该会想到HTTP代理。HTTP代理是指位于HTTP服务器(Web服务器)和HTTP客户端(Web浏览器)之间,为Web页面提供高速缓存等功能的软件。我们也可以认为它是一种Proxy模式。
HTTP代理有很多功能。
通过Web浏览器访问Web页面时,并不会每次都去访问远程Web服务器来获取页面的内容,而是会先去获取HTTP代理缓存的页面。只有当需要最新页面内容或是页面的缓存期限过期时,才去访问远程Web服务器。
在这种情况下,Web浏览器扮演的是Client角色,HTTP代理扮演的是Proxy角色,而Web服务器扮演的则是RealSubject角色。
各种Proxy模式
1.Virtual Proxy(虚拟代理)
Virtual Proxy 就是本章中学习的Proxy模式。只有当真正需要实例时,它才生成和初始化实例。
2.Remote Proxy(远程代理)
Remote Proxy可以让我们完全不必在意RealSubject角色是否在远程网络上,可以如同它在自己身边一样(透明性)调用它的方法。Java的RMI(RemoteMethodInvocation:远程方法调用)就相当于Remote Proxy。
3.Access Proxy (访问代理)
Access Proxy用于在调用RealSubject角色的功能时设置访问限制。例如,这种代理可以只允许指定的用户调用方法,而当其他用户调用方法时则报错。