代理:为其他对象提供一种代理以控制对这个对象的访问。
对应的代理业务图:
注(以下关于“代理对象”的图标采用“←”标识)
这里我们将B看做是“被代理对象”,箭头←看做是“代理对象”。
代理模式的uml图:
对应的代码展示:
public class Client
{
public static void main(String[] args)
{
Proxy daili=new Proxy();
daili.GiveDolls();
daili.GiveFlowers();
daili.GiveChocolate();
}
}
public interface IGiveGift {
void GiveDolls();
void GiveFlowers();
void GiveChocolate();
}
public class Pursuit implements IGiveGift {
public Pursuit(){
}
@Override
public void GiveDolls() {
System.out.println("送你洋娃娃");
}
@Override
public void GiveFlowers() {
System.out.println("送你鲜花");
}
@Override
public void GiveChocolate() {
System.out.println("送你巧克力");
}
}
public class Proxy implements IGiveGift {
private Pursuit gg;
public Proxy(){
}
public Proxy(){
gg=new Pursuit();
}
@Override
public void GiveDolls() {
gg.GiveDolls();
}
@Override
public void GiveFlowers() {
gg.GiveFlowers();
}
@Override
public void GiveChocolate() {
gg.GiveChocolate();
}
}
在Proxy类中将被代理对象Pursuit作为Proxy类的成员变量,从而来控制对Pursuit对象的访问。此时代理对象与被代理对象之间是关联关系,耦合性较高。
思考以上代理模式已经可以完成代理业务了,为什么后面会有动态代理?静态代理的缺点是什么?动态代理如何解决静态代理中的缺点?jdk的动态代理又是如何做的?有了jdk的动态代理怎么还有cglib的动态代理呢?
带着以上问题我们来逐一分析。
在以上代码的基础上,随着客户需求的增加,现在想要增加被代理类,要如何操作?
方式一:一个代理类代理多个被代理对象,在Proxy类中添加被代理类。
这样做会破坏我们的开闭原则,并且会导致代理类庞大。
方式二:为每个被代理类单独创建代理对象。
这样会导致类剧增,并且重复代码过多,存在冗余。
那要如何解决呢?
做架构师的人或者是精通设计模式的人都应该知道,我们解耦的核心在于--抽象!
在面对用户需求变更经常发生变动的代码来说,我们首先要想到的就是将变与不变分离。即将不变的部分抽象出来,将变的部分以参数的形式传入。
以上静态代理中经常要发生变化的是Proxy类中的Pursuit类,也就是被代理类,以及被代理类所实现的接口方法。
而不变的是代理的思想,即代理类在执行方法时,并没有自己的实现方法,而是调取的被代理对象的方法。
这样我们就将变与不变进行了划分。
那具体的代码是如何体现的呢?我们以JDK的动态代理为例。
宏观的uml图(以下uml图中我们可以看到B(被代理对象),而←(代理对象)则是动态生成的,所以在此未展示)
对应代码:
public class Client {
public static void main(String[] args) {
//在文件夹中显示自动生成的.class文件
System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//实例化“调用处理程序”,并将“目标对象"作为参数传入,由此可以让“目标对象”去做事情
SubjectHandler subjectHandler=new SubjectHandler(new RealSubject());
//实例化代理类,该“代理类”实现与“目标类”同样的接口
ISubject proxySubject=(ISubject)Proxy.newProxyInstance(
subjectHandler.getClass().getClassLoader(),
new RealSubject().getClass().getInterfaces(),
subjectHandler);
//通过代理类调用“请求方法”
proxySubject.request();
}
}
public interface ISubject {
/**
* 发起请求的方法
*/
void request();
}
public class RealSubject implements ISubject {
/**
* 目标对象发起请求的方法
*/
@Override
public void request() {
System.out.println("发起请求");
}
}
public class SubjectHandler implements InvocationHandler {
//设置一个用来接收“目标对象”的成员变量
Object target;
//实例化“调用处理程序”时,将“目标对象”作为参数传入
public SubjectHandler(Object target){
this.target=target;
}
//实现“电调用处理程序”的invoke方法,来执行客户端通过“代理类”调用的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过反射调取传入的method方法,并将method方法执行后的结果返回
return method.invoke(this.target,args);
}
}
接着我们上面的静态代理来说,不变的部分是代理类所执行的方法是通过调取被代理类的方法来运行的,实际还是被代理类去做事情。
不变的体现:
变的体现:
通过上面的代码我们还可以发现,在静态代理里面被代理对象B与代理对象←是关联关系,而到了JDK中则是通过InvocationHandler接口进行了隔离,降低了被代理对象B与代理对象←之间的耦合,这样代理就可以毫无波澜的拥抱变化了。