代理(Proxy)是一种设计模式,它提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
本文我们介绍静态代理。静态代理在使用时,需要定义接口或者父类,目标对象与代理对象一起实现相同的接口或者是继承相同父类。
例如,现在我们想举办一场演唱会,邀请一名歌星前来助阵。一种方案是,我们去寻找演艺经纪公司,让他们帮我们联系歌手出演节目。这里,歌手是目标对象,他只负责在演唱会上唱歌;而演艺公司则是代理对象,他们还要负责除了歌手唱歌之外其它的琐事。使用Java程序实现如下:
public class Main {
public static void main(String[] args) {
// can be simplified as
// new Agency(new Singer()).sing();
Singer singer = new Singer();
Agency agency = new Agency(singer);
agency.sing();
}
}
interface Concert {
void sing();
}
class Singer implements Concert {
@Override
public void sing() {
System.out.println("Do La Mi");
}
}
class Agency implements Concert {
private Concert target;
public Agency(Concert target) {
this.target = target;
}
@Override
public void sing() {
System.out.println("Before concert: Announcement");
target.sing();
System.out.println("After concert: Payment");
}
}
代码中,类Singer(歌手)和Agency(演艺经纪公司)都实现了Concert接口(演唱会),只是Singer类里重写的sing方法只有“唱歌”这一件事,而Agency类里重写的sing方法除了调用target对象的sing方法外,还要做其它事情(如会前宣传、会后收账)。不难看出,Agency类里的sing方法增强了Singer类里的sing方法,完成了Singer类所不能完成的任务。
回顾先前介绍的实现Runnable接口的线程创建方式:
public class Main {
public static void main(String[] args) {
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
}
}
class Task implements Runnable {
@Override
public void run() {
System.out.println("Working");
}
}
代码中main方法里,第一句创建的Task类对象实现了Runnable接口,是我们的目标对象(类比于上个例子中的歌手);第二句,我们将task对象丢入Thread类的构造方法中创建了一个线程对象thread,它是我们的代理对象(类比于上个例子中的经纪公司);第三句,我们调用thread的start方法开启线程,事实上就是由代理对象完成线程的开启及运行的(在Thread类的start方法的源码中,程序让JVM调用run方法,而run方法就是直接调用目标对象的run方法)。
总结起来,静态代理可以做到在不修改目标对象的功能前提下,对目标功能扩展。但是,因为代理对象需要与目标对象实现一样的接口/继承同一个父类,所以会产生很多代理类,使得类太多;同时,一旦接口增加方法,目标对象与代理对象都要维护。