×

【设计模式笔记】(十六)- 代理模式

96
MrTrying
2018.06.24 12:18* 字数 1327

一、简述

代理模式(Proxy Pattern),为其他对象提供一个代理,并由代理对象控制原有对象的引用;也称为委托模式。

其实代理模式无论是在日常开发还是设计模式中,基本随处可见,中介者模式中的中介者对象也是代理模式的应用,其他的对象的交互都是交给了中介者对象处理的。而在生活中就更多类似代理模式的例子,例如:抢票插件、科学上网等等。

代理模式
  • Subject:抽象主题类(也可以是接口),声明共同方法
  • RealSubject:真实主题类,也就是被代理类,负责执行具体的业务逻辑方法;客户类调用代理类简介调用其定义的方法
  • ProxySubject:代理主题类,也就是代理类,持有一个被代理类的真是对象,在实现抽象主题类的共同方法中调用被代理类相应的方法,起到代理的作用
  • Client:客户类,使用代理对象的类
/**抽象主题*/
public interface Subject {
    public void visit();
}

/**被代理主题*/
public class RealSubject implements Subject {
    @Override
    public void visit() {
        System.out.print("this is real subject");
    }
}

/**代理主题*/
public class ProxySubject implements Subject {
    //真实主题
    private Subject realSubject;

    public ProxySubject(@NotNull Subject subject){
        realSubject = subject;
    }

    @Override
    public void visit() {
        System.out.print("proxy start");
        realSubject.visit();
        System.out.print("proxy end");
    }
}

/**客户类*/
public class Client {
    public static void main(String[] args){
        //创建被代理对象
        RealSubject realSubject = new RealSubject();
        //创建代理对象
        ProxySubject proxySubject = new ProxySubject(realSubject);
        //调用方法
        proxySubject.visit();
    }
}

输出结果:
proxy start
this is real subject
proxy end

二、动态代理

上一部分中所讲述的,其实是静态代理,也就是在代码的编译阶段生成代理类来完成代理对象的一系列操作。而动态代理则是在运行时动态生成代理类对象。代理对象的生成是利用JDKjava.lang.reflect.Proxy类,使用newProxyInstance方法可以创建一个我们所需要的代理对象

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
//代码省略...
}

newProxyInstance方法是Proxy的一个静态方法,并且接收三个参数

  • ClassLoader loader:类加载器
  • Class<?>[] interfaces:目标对象实现的接口的类型
  • InvocationHandler h:处理事件的对象,InvocationHandler是一个接口,执行目标对象的方法,会触发InvocationHandlerinvoke方法

其中InvocationHandlerinvoke方法如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;`

  • Object proxy:执行方法的代理对象
  • Method method:被执行的方法的对象
  • Object[] args:被执行的方法的参数
    返回值则是代理对象调用方法时所返回的值

依然使用Subject作为例子,看看简单的代码实现(代码没有封装)

//创建被代理对象
Subject realSubject = new RealSubject();
//创建代理对象
Subject subject = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
    new Class[]{Subject.class},
    new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(realSubject, args);
        }
    });
subject.visit();

从代码上来看,比之前的静态代理要简单很多,没有那么多的类和对象;但是相对的在代码性能上有所牺牲,而且对于不太熟悉反射相关的知识的开发者并不是太友好。

通过反射类ProxyInvocationHandler回调接口实现的JDK动态代理,要求被代理类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

三、其他代理分类

静态代理和动态代理是代码方面来区分的代理模式,也可以从适用范围来区分不同类型的代理实现

  • 远程代理(Remote Proxy):为某个对象在不同的内存地址空间提供局部代理。使系统可以将Server部分的实现隐藏,以便Client不必考虑Server的存在
  • 虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗资源的对象并在真正需要时才创建
  • 保护代理(Protection Proxy):使用代理控制对原始对象的访问。该类型的代理常被用于原始对象有不同访问权限的情况。
  • 智能引用(Smart Reference):在访问原始对象时执行一些自己的附加操作并对指向院士对象的引用计数

这里要注意的是,静态和动态代理都可以应用于上述4种情形,两者是各自独立的变化。

四、总结

代理模式使用非常广泛,基本在其他的设计模式中也能看到代理模式的影子,但是使用时针对性较强,而且模式本身并没有什么突出的优缺点,基本上可以放心使用

PS:从此你写过的代码都像代理模式

「推荐」设计模式系列

设计模式(零)- 面向对象的六大原则
设计模式(一)- 单例模式
设计模式(二)- Builder模式
设计模式(三)- 原型模式
设计模式(四)- 工厂模式
设计模式(五)- 策略模式
设计模式(六)- 状态模式
设计模式(七)- 责任链模式
设计模式(八)- 解释器模式
设计模式(九)- 命令模式
设计模式(十)- 观察者模式
设计模式(十一)- 备忘录模式
设计模式(十二)- 迭代器模式
设计模式(十三)- 模板方法模式
设计模式(十四)- 访问者模式
设计模式(十五)- 中介者模式
设计模式(十六)- 代理模式
设计模式(十七)- 组合模式
【设计模式笔记】(十八)- 适配器模式
【设计模式笔记】(十九)- 装饰者模式

设计模式
Web note ad 1