基本介绍 – 只在必要时生成实例
- 代理模式:为一个对象提供一个替身,以控制对这对象的访问。即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
- 被代理的对象可以是远程对象、创建开销大的对象需要安全控制的对象
只在必要时生成实例,我在看网上的详解中,大部分没有提到这句话,只讲了代理而已。
在面向对象编程中,“本人”(被代理)和“代理人”(代理)都是对象,如果“本人”对象太忙了,有些工作无法亲自完成,就将其交给“代理人”对象负责。
示例程序
实现一个带名字的打印机,说是打印机,只是将文字显示在界面上而已。
类和接口一览表:
- Printer:表示带名字的打印机(被代理)
- Printable:Printer和PrintProxy的共同接口
- PrintProxy:表示带名字的打印机(代理人)
- Main;测试程序行为的类
类图:
时序图:
Printer:表示带名字的打印机(被代理)
public class Printer implements Printable{
//打印机的名字
private String name;
//构造方法内做一些“重活”,表示“本人”对象太忙了。。
public Printer() {
heavyJob("Print实例生成中");
}
public Printer(String name) {
this.name = name;
heavyJob("Print实例生成中(" + name + ")");
}
@Override
public void setPrintName(String name){
this.name = name;
}
@Override
public String getPrintName(){
return name;
}
//显示一串打印机的名字和打印内容
@Override
public void print(String string){
System.out.println("=== " + name + " ===");
System.out.println(string);
}
//sleep方法,表示5秒的“重活”
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.println("结束。");
}
}
Printable:Printer和PrintProxy的共同接口
public interface Printable {
void setPrintName(String name);
String getPrintName();
void print(String string);
}
PrintProxy:表示带名字的打印机(代理人),不论setPrintName和getPrintName方法别调用多少次,都不会生成Printer实例,只有调用代理人无法完成的print方法时,才会生成实例。
Print类并不知道PrintProxy类的存在,即,Printer类不知道自己到底是通过PrinterProxy调用还是直接被调用的
但反过来,PrinterProxy类是知道Printer类的,这是因为Printer类是PrinterProxy类的字段,显式的写出了Printer这个类名,Printer和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 setPrintName(String name) {
//当打印机对象存在时,需将打印机对象内的名字一起同步更新
if(real != null){
real.setPrintName(name);
}
this.name = name;
}
@Override
public String getPrintName() {
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 proxy = new PrinterProxy("Alice");
System.out.println(proxy.getPrintName());
proxy.setPrintName("Job");
System.out.println(proxy.getPrintName());
proxy.print("Hello, world");
}
}
适用场景:
- 延迟初始化 (虚拟代理)。 如果你有一个偶尔使用的重量级服务对象, 一直保持该对象运行会消耗系统资源时, 可使用代理模式。你无需在程序启动时就创建该对象, 可将对象的初始化延迟到真正有需要的时候。
- HTTP代理。通过浏览器区访问Web界面,并不会每次都去访问Web服务器来获取页面,而是先去获取HTTP代理缓存的页面。只有当需要最新页面或页面的缓存期限过期时,才回去访问远程Web服务器。
- 访问控制 (保护代理)。 如果你只希望特定客户端使用服务对象, 这里的对象可以是操作系统中非常重要的部分, 而客户端则是各种已启动的程序 (包括恶意程序), 此时可使用代理模式。 代理可仅在客户端凭据满足要求时将请求传递给服务对象。
- 记录日志请求 (日志记录代理)。 适用于当你需要保存对于服务对象的请求历史记录时。 代理可以在向服务传递请求前进行记录。