Proxy模式——只在必要时生成实例
Proxy模式中,为主类创建了一个代理类,一些不一定需要主类完成的工作,可以由代理类来完成,只有代理类无法解决的任务才交由主类完成。
下面的示例程序是一个打印机的模拟。
由于打印机的初始化时间很长(假设需要的初始化时间为5秒钟),当一些不需要打印的工作来临时,交由打印机的代理类来完成,只有开始进入实际打印阶段后,才会生成打印机的实例。
为了让主类和代理类具有一致性,还需要定义一个统一的接口。
下面是示例程序的具体实现。
- 类和接口的一览表
名字 | 说明 |
---|---|
Printable | Printer和PrinterProxy的共同接口 |
Printer | 表示打印机的类(主类) |
PrinterProxy | 表示打印机的类(代理类) |
Main | 测试程序行为的类 |
- Printable接口
public interface Printable {
public abstract void setPrinterName(String name);
public abstract String getPrinterName();
public abstract 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 + ")");
}
private void heavyJob(String msg) {
System.out.println(msg);
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(".");
}
System.out.println("结束。");
}
@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;
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("1号");
System.out.println("现在的打印机是" + p.getPrinterName());
p.setPrinterName("2号");
System.out.println("现在的打印机是" + p.getPrinterName());
p.print("Hello, world.");
}
}
示例输出如下。
现在的打印机是1号
现在的打印机是2号
正在生成Printer的实例(2号)
.....结束。
===2号===
Hello, world.
Proxy模式中的角色
- Subject(主体)
Subject角色定义了使Proxy角色和RealSubject角色之间具有一致性的接口。由于存在Subject角色,所以Client角色不必在意它所使用的究竟是Proxy角色还是RealSubject角色。在示例程序中,由Printable接口扮演此角色。
- Proxy(代理人)
Proxy角色会尽量处理来自Client角色的请求。只有当自己不能处理时,它才会将工作交给RealSubject角色。Proxy角色只有在必要时才会生成RealSubject角色。Proxy角色实现了在Subject角色中定义的接口(API)。在示例程序中,由PrinterProxy类扮演此角色。
- RealSubject(实际的主体)
RealSubject角色会在Proxy角色无法解决任务时出场解决任务,它也实现了Printable接口。在示例程序中,由Printer类扮演此角色。
- Client(请求者)
使用Proxy模式的角色。在示例程序中,由Main类扮演此角色。
Proxy模式的思路
使用代理人来提升处理速度
如果在一个大型系统的初始化过程中,存在大量的耗时处理。如果在启动系统时连那些暂时也不会被使用的功能也初始化了,那么应用程序的启动时间将会非常漫长。而如果我们只在需要使用某个功能时才将其初始化,则可以帮助我们改善用户体验。
区分代理类和主类
我们也可以不划分代理类和主类,而是直接在主类中加入惰性求值功能。但是,通过划分代理类和主类,可以使它们形成独立的组件,即使在进行修改时也不会相互影响。而且请求者可以随意地切换代理类和主类,以实现想要达到的效果。