设计模式之代理模式

该篇讲主要讲述我学习代理模式的心得记录。

代理模式基本属性

1、定义

一个类可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文档或其它昂贵或无法复制的资源。而代理又分为静态代理和动态代理,其中动态代理具有更强的灵活性,不用在我们设计实现的时候就指定某一个代理类来代理哪一个被代理对象,而是把这种实现推迟到进程运行时。

2、应用场景

功能需要被授予不同的权限时,如,注册与未注册的用户。

3、优点

一个复杂对象可以由多个代理来引用,这样便节省了系统资源。

实例讲解

1、静态代理

首先定义一个抽象类AbstractObject并在其内部定义一个抽象的方法,作为代理和被代理类的父类,

 public abstract class AbstractObject {
  public abstract void operation();
}

,然后创造一个继承了此父类的被代理类,

public class RealObject extends AbstractObject {
  @Override
  public void operation(){
      System.out.println("some operation");
 }
 }

再创造一个代理类,

 public class ProxyObject extends AbstractObject {
   RealObject realObject = new RealObject();
 @Override
     public void operation() {
       System.out.println("before");
       realObject.operation();
       System.out.println("after");
   }
}

在代理类中创建了一个父类,并且在重写的方法中调用了被代理类的同名方法。这便实现了一个简单的静态代理。

回顾一下思路,它实现一个共有的抽象类,代理与被代理均继承于这个类,在代理类中构造被代理类作为一个私有成员,并且在重写的方法中调用被代理类中重写的方法,并且可在该方法中添加自己的行为,最后客户端调用代理类中他们的共有接口,实现代理操作。

感觉和装饰者模式有些类似,均实现了相同的接口,通过调用底层的类来实现他们的功能。不过装饰者模式是将被装饰者作为参数传入,而静态代理是作为自己的私有成员变量。

2、动态代理

我们采用定义接口的方式来实现,首先定义一个接口AbstractSubject,

 public interface AbstractSubject {
  public abstract void request();
}

接着就是我们被代理的对象,

public class RealSubject implements AbstractSubject {
 @Override
 public void request()
 {
     System.out.println("real subject's request()........");
 }
}

在被代理类中我们重写了接口中定义的方法,接着再实现最重要的代理类,

public class DynamicProxy implements 
InvocationHandler {
     Object beProxy = null;
 public DynamicProxy(Object beProxy){
     this.beProxy = beProxy;
    }
     @Override
   public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
       System.out.println("before ----------"+method);
      Object result = method.invoke(this.beProxy,objects);
      System.out.println("after ----------"+method);
        return result;
  }
}

在这个代理类中,我们有一个需要传入被代理类对象的构造方法,而它实现的接口是系统自带的InvocationHandler,并且重写了invoke()方法。最后,我们来看一下,我们是怎么使用这个代理的,

  public class DynamicProxyClient {
    public static void main(String[] args) {
        AbstractSubject realSubject = new RealSubject();
        ClassLoader loader = realSubject.getClass().getClassLoader();
      Class<?>[] interfaces = realSubject.getClass().getInterfaces();

     InvocationHandler dynamicProxyHandler = new DynamicProxy(realSubject);

AbstractSubject proxy = (AbstractSubject) Proxy.newProxyInstance(loader, interfaces, dynamicProxyHandler);
        proxy.request();
    }
 }

    
使用java原生的Proxy类与InvocationHandler接口,通过自己所写的DynamicProxy实现InvocationHandler接口,在进程运行时,newProxyInstance()方法通过接收类加载器ClassLoader,动态代理类所实现的接口interface以及实现了InvocationHandler接口的动态类实例DynamicProxy,创建一个动态代理proxy。

其中,在DynamicProxy类中重写的invoke()方法,则是动态代理所必须执行的,因为Proxy.newProxyInstance()方法返回的是一个继承于Proxy的子类对象,通过调用该对象的代理方法,会执行父对象中InvocationHandler成员的invoke()方法,因此,最终执行了我们在DynamicProxy中重写的invoke()方法。关于最后proxy调用request()方法为什么就能执行到被代理类的方法,这里我再做一次说明。因为proxy是属于Proxy类的,因为他的类是com.sun.proxy.$Proxy0,是继承了Proxy类的,而在$Proxy0中重写了request()方法,并且调用了父类中h的invoke方法,而在父类Proxy中,h是这样定义的,

protected InvocationHandler h;

也就是说调用的是InvocationHandler.invoke()方法,而DynamicProxy又实现了InvocationHandler接口的invoke()方法,因此,最后调用的便是DynamicProxy中的invoke()方法。这也就是java内置的动态代理实现机制。

总结

学习了该模式之后,有了些感悟。设计模式,究其本质,就是一些方式方法,并且是适合面向对象这种语言的方法,我们通过这些方法能够顺利的达到我们所需要的目的,并且能够让我们的代码结构变得更清晰,更具逻辑。这便是设计模式的目的吧。