Java设计模式 — 代理模式(Proxy)

代理模式(Proxy)

Proxy模式,也称代理模式,是经典设计模式中的一种结构型模式,其定义是为其他对象提供一种代理以控制对这个对象的访问,代理对象起到了中介作用,不涉及功能服务,亦可增加额外的服务。通俗来说就是,为其他对象提供代理对象,以控制对这个对象的访问。

代理模式的分类

  • 远程代理:为不同的地理对象提供局域网代表对象。典型的设计有:C/S架构属于远程代理的缩影
  • 虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候再创建。典型设计:经常我们看到很多APP在加载图片的时候,会先加载一个默认的图片,等真正的图片加载完了之后再显示出来,这样非常的友好。
  • 智能引用代理:提供对目标对象的额外的服务。典型设计:现实场景中随处可见,我们的火车、汽车票代售处、代购等等都是属于代理模式的范畴。
  • 保护代理:控制用户的访问权限。典型设计:就像我们的公众号的文章留言功能,只有你这个用户关注了该公众号之后才能留言,否则你就只能浏览不能留言。

静态代理

代理和被代理对象在代理之前都是被确定的,他们都实现相同的接口或者是继承相同的抽象类,这里一张经典的图
在这里插入图片描述上面这张图我们可以看出:ProxySubject和RealSbuject都是继承了Subject,Client请求Subjct的doSomething()方法,本应该是调用RealSubject中的doSomething()方法,但是我们实际看到的却是调用的ProxySubject方法中的doSomething()方法,在ProxySubject方法中的doSomething()方法中再去调用RealSubject中的doSomething()方法。我们实际看到的是ProxySubject方法中的doSomething()方法,这样就完成了代理。

简单实例

1.共同实现的接口

public interface  Subject{
   public void doSomeThing();
}

2.被代理对象,逻辑执行者

public class RealSubject implements Subject{
   @Override
   public void doSomeThing() {
       //do some things
   }
}

3.代理类

我们在构造方法里面创建了一个RealSubjecj的实现类对象,在调用Proxy的doSomething()方法的时候,实际是调用的RealSubject对象的的doSomething()方法,但是经常我们还会附加一些逻辑在里面比如我们需要打印一些Log日志,直接这样加就可以了:

public class Proxy implements Subject{
   private Subject subject = null;
   public Proxy(){
       subject = new RealSubject();
   }
   @Override
   public void doSomeThing() {
       log.d("TAG","代理开始...")
       subject.doSomeThing();
       log.d("TAG","代理开结束...")
   }
}

这样的好处就是正在实现类的逻辑不用做任何改变即可实现。

动态代理

动态代理有以下特点:

  1. 代理对象,不需要实现接口
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

JDK中InvocationHandler接口与Proxy类

InvocationHandler类

InvocationHandler 是一个接口,官方文档解释说:每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler是个接口,只有一个抽象方法 invoke(Object proxy, Method method, Object[] args),四个参数的意义:

  • param proxy 代理类
  • param method 被代理的方法
  • param args 被代理方法的参数
  • return 方法的返回值

Proxy类

JDK通过 Proxy 的静态方法 newProxyInstance 动态地创建代理,该方法在Java中的声明如下

    /**     
     * @description 
     * @author rico       
     * @created 2017年7月3日 下午3:16:49     
     * @param loader 类加载器
     * @param interfaces 目标类所实现的接口
     * @param h  InvocationHandler 实例
     * @return     
     */
    public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)

事实上,Proxy 动态产生的代理对象调用目标方法时,代理对象会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

简单示例

1.接口

同样地,首先得有一个接口,通用的接口是代理模式实现的基础。

public interface ISubject {
   //出行
   public void doSomething();
}

2.被代理角色

然后,我们要有一个真正的实现这个ISubject接口的类,以便代理。

public class RealSubject implements ISubject {
   private static final String TAG = "RealSubject";

   @Override
   public void doSomething() {
       Log.e(TAG, "doSomething: 执行中");
   }
}

3.代理类

在动态代理中,代理类及其实例是程序自动生成的,因此我们不需要手动去创建代理类。在Java的动态代理机制中,InvocationHandler(Interface)接口和Proxy(Class)类是实现我们动态代理所必须用到的。事实上,Proxy通过使用InvocationHandler对象生成具体的代理代理对象,下面我们看一下对InvocationHandler接口的实现:

/**
* Created by Scorpio on 2018/7/9.
* 该类经常称之为事务处理类
*/

public class DynamicProxy implements InvocationHandler {
   private static final String TAG = "DynamicProxy";
   // 被代理对象
   private Object target;

   public DynamicProxy(Object target) {
       this.target = target;
   }

   /**
    * @param proxy  代理类
    * @param method 被代理的方法
    * @param args   被代理方法的参数
    * @return 返回代理对象
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       Log.e(target.getClass().getName(), "开始日志。。。");
       method.invoke(target);
       Log.e(target.getClass().getName(), "结束日志。。。");
       return null;
   }
}

4.客户端

在实现了InvocationHandler接口后,我们就可以创建代理对象了。在Java的动态代理机制中,我们使用Proxy类的静态方法newProxyInstance创建代理对象,如下:

public class MainActivity extends AppCompatActivity {
   private static final String TAG = "MainActivity";

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Log.e(TAG, "===================RealSubject========================= ");
       RealSubject realSubject = new RealSubject();
       DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
       ISubject proxyInstance = (ISubject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), dynamicProxy);
       proxyInstance.doSomething();
   }
}

输出

07-10 00:07:30.284 30241-30241/com.lt.proxy E/MainActivity: ===================RealSubject========================= 
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 开始日志。。。
07-10 00:07:30.285 30241-30241/com.lt.proxy E/RealSubject: doSomething: 执行中
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 结束日志。。。

总结

优点:

  1. 代理模式能够协调调用者和被调用者,在一定程度上降低了系统的耦合度;
  2. 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了保护目标对象的作用。

缺点:

  1. 由于在客户端和真实对象之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢;
  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂。
发布了63 篇原创文章 · 获赞 1 · 访问量 2101

猜你喜欢

转载自blog.csdn.net/weixin_42046829/article/details/104922820