设计模式 - 代理模式

简介

代理模式是软件设计模式中的一种,它提供了一个代理对象来代表另一个对象。这样做的目的是为了控制对原始对象的访问,即在不直接暴露原始对象的情况下,通过代理对象间接操作原始对象。代理模式有多种实现方式,其中最常见的是静态代理和动态代理。

静态代理

在静态代理中,代理类是预先定义好的,它与被代理类实现相同的接口,并且在内部持有被代理类的引用。由于代理类和被代理类需要实现相同的接口,因此如果接口有很多方法,会导致代理类有大量重复的代码

动态代理

动态代理不需要在代码中显式编写代理类,而是在程序运行时,通过反射机制动态创建。Java 中的 java.lang.reflect.Proxy 类和 java.util.function.Function 接口可以用来创建动态代理对象。动态代理允许在运行时指定一个或多个接口,然后为这些接口的方法创建代理方法。

区别:动态代理更加灵活,可以在运行时动态地创建代理类,适应不断变化的需求。静态代理由于是编译时生成的,可能在性能上略优于动态代理,后者需要在运行时动态生成代理类。

静态代理

目标类把要代理的方法放到接口中,代理类和目标类实现同一个接口,然后代理类通过构造方法和目标类建立联系,在业务中通过目标类对象调用目标方法的同时,代理类可以执行自己的逻辑。

提供公共接口 Calculator

public interface Calculator {

    int add(int i,int j);

}

准备目标类,可以看到目标类仅仅做了加法运算。

public class CalculatorImpl implements Calculator{
    @Override
    public int add(int i, int j) {
        return i + j;
    }
}

准备代理类,实现同一个接口 Calculator 并且引用目标类

public class StaticProxyCalculator implements Calculator {

    private Calculator calculator;

    public StaticProxyCalculator(Calculator calculator) { // 与目标类建立联系
        this.calculator = calculator;
    }

    @Override
    public int add(int i, int j) {
        System.out.println("i = " + i + ", j = " + j);
        int result = calculator.add(i, j);
        System.out.println("result = " + result);
        return result;
    }
}

现在我们创建客户端

public class Client {

    public static void main(String[] args) {
        Calculator calculator = new CalculatorImpl(); // 创建目标类
        Calculator proxy = new StaticProxyCalculator(calculator); // 创建代理类
        proxy.add(1, 1); // 代理去做事
    }

}

输出结果:从输出的结果可以发现,代理类做的事情不仅完成了目标类的功能,还在此的基础上做出了打印输出,起到一定程度上的增强效果。

i = 1, j = 1
result = 2

总结:一句话说静态代理的优缺点就是简单,但是麻烦。

动态代理

动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。我们这里就是用 Java 反射包下原生的代理工具类即可。其他的还有 CGLIB 代理。

示例:假设我们有一位女明星,她负责唱歌和跳舞,现在她要把这两项工作代理给助手负责准备话筒和场地。

准备一个公用接口,该接口暴露出去的是准备代理的方法。

public interface Star {

    String sing(String singName);
    void dance();

}

准备女明星类,负责唱歌和跳舞

public class BigStar implements Star {

    private String name;

    public BigStar(String name) {
        this.name = name;
    }

    @Override
    public String sing(String singName) {
        System.out.println(this.name + "正在唱:" + singName);
        return "谢谢";
    }

    @Override
    public void dance() {
        System.out.println(this.name + "正在跳优美的舞蹈");
    }
}

准备代理的工具类,负责代理 Star 中的方法

public class ProxyUtil {

    public static Star createProxy(BigStar bigStar){
        Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("sing")) {
                    System.out.println("准备话筒"); // 代理唱歌
                }
                if (method.getName().equals("dance")) {
                    System.out.println("准备跳舞"); // 代理跳舞
                }
                return method.invoke(bigStar, args); // 女明星自己去唱歌跳舞
            }
        });
        return starProxy; // 返回代理好的结果
    }

}

客户端:

public class Client {

    public static void main(String[] args) {
        BigStar bigStar = new BigStar("杨超越"); // 创建女明星对象
        Star starProxy = ProxyUtil.createProxy(bigStar); // 代理后的结果

        // 代理后的唱歌
        System.out.println(starProxy.sing("燃烧卡路里"));
        // 代理后的跳舞
        starProxy.dance();
    }

}

输出结果:

准备话筒
杨超越正在唱:燃烧卡路里
谢谢
准备跳舞
杨超越正在跳优美的舞蹈

总结

静态代理比动态代理更符合OOP原则,在日常开发中使用也较多。动态代理在开发框架时使用较多,例如大名鼎鼎的 Spring

动态代理在AOP中运用比较广泛,比如日志记录,性能统计,安全控制,事务处理,异常处理等场景下。

你可能感兴趣的:(设计模式,代理模式,java,动态规划)