代理模式

2023-04-06  本文已影响0人  长点点

一、基本概念

代理模式是一种设计模式,它的定义是:为其他对象提供一种代理以控制对这个对象的访问。代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

代理模式的主要优点有:

代理模式的主要缺点是:

代理模式的主要角色有:

根据代理类创建时机的不同,代理模式可以分为静态代理和动态代理。

静态代理是指在编译时就已经确定了代理类和被代理类之间的关系,代理类需要实现与被代理类相同的接口,并在内部调用被代理类的方法,并且可以添加一些额外的处理逻辑。静态代理的优点是简单易用,缺点是不够灵活,每一个被代理类都需要一个对应的代理类。

动态代理是指在运行时根据被代理类动态生成对应的代理类,不需要事先编写代码。动态代理可以利用反射机制或者第三方库来实现。动态代理的优点是更加灵活和智能,可以根据不同的被代理类生成不同的代理类,并且可以在运行时修改或增加方法。动态代理的缺点是相对复杂,需要掌握反射机制或者第三方库的使用。

代理模式的uml图如下:


uml图

二、安卓源码中的实例

在安卓开发中,有很多地方使用了代理模式,例如:

三、Kotlin、Java和C++实现

代理模式可以用不同的编程语言来实现,下面我们分别用Kotlin、Java和C++来举例说明。

3.1 Kotlin实现

假设我们有一个打印机接口,定义了打印文本和图片的方法:

interface Printer {
    fun printText(text: String)
    fun printImage(image: String)
}

然后我们有一个真实的打印机类,实现了打印机接口:

class RealPrinter : Printer {
    override fun printText(text: String) {
        println("RealPrinter prints text: $text")
    }

    override fun printImage(image: String) {
        println("RealPrinter prints image: $image")
    }
}

接下来,我们想要在打印之前和之后添加一些日志信息,但是又不想修改真实打印机类的代码,这时候就可以用代理模式来实现。我们可以定义一个代理打印机类,也实现了打印机接口,并持有一个真实打印机对象的引用:

class ProxyPrinter(private val printer: Printer) : Printer {
    override fun printText(text: String) {
        println("ProxyPrinter starts printing text")
        printer.printText(text)
        println("ProxyPrinter finishes printing text")
    }

    override fun printImage(image: String) {
        println("ProxyPrinter starts printing image")
        printer.printImage(image)
        println("ProxyPrinter finishes printing image")
    }
}

这样,我们就可以通过代理打印机对象来调用真实打印机对象的方法,并在前后添加日志信息:

fun main() {
    val realPrinter = RealPrinter()
    val proxyPrinter = ProxyPrinter(realPrinter)
    proxyPrinter.printText("Hello World")
    proxyPrinter.printImage("Kotlin.png")
}

输出结果如下:

ProxyPrinter starts printing text
RealPrinter prints text: Hello World
ProxyPrinter finishes printing text
ProxyPrinter starts printing image
RealPrinter prints image: Kotlin.png
ProxyPrinter finishes printing image

这是一种静态代理的实现方式,我们需要手动编写代理类,并实现所有需要代理的方法。如果我们想要更简洁和灵活的方式,我们可以使用Kotlin提供的委托模式。委托模式是指一个类可以将某些方法或属性的实现委托给另一个对象,而不是自己去实现。Kotlin提供了by关键字来支持委托模式。

使用委托模式,我们可以省略代理类的定义,而直接在创建代理对象时指定委托对象,并重写需要添加额外操作的方法:

fun main() {
    val realPrinter = RealPrinter()
    val proxyPrinter = object : Printer by realPrinter {
        override fun printText(text: String) {
            println("ProxyPrinter starts printing text")
            realPrinter.printText(text)
            println("ProxyPrinter finishes printing text")
        }

        override fun printImage(image: String) {
            println("ProxyPrinter starts printing image")
            realPrinter.printImage(image)
            println("ProxyPrinter finishes printing image")
        }
    }
    proxyPrinter.printText("Hello World")
    proxyPrinter.printImage("Kotlin.png")
}

输出结果与上面相同。

这是一种动态代理的实现方式,我们不需要提前创建代理类,而是利用反射机制在运行时创建代理对象,并动态地添加额外的操作。这样可以减少代码量,并提高可扩展性。

3.2 Java实现

Java是一种广泛使用的面向对象编程语言,它也支持代理模式的实现,但是没有Kotlin那么简洁和灵活。Java中的代理模式主要分为静态代理和动态代理两种。

静态代理的实现方式与Kotlin类似,我们需要定义一个接口,一个真实主题类和一个代理主题类,代理主题类持有真实主题类的引用,并实现接口中的所有方法,在调用真实主题类的方法前后添加额外的操作。例如:

// 定义一个打印机接口
public interface Printer {
    void printText(String text);
    void printImage(String image);
}
// 定义一个真实的打印机类,实现打印机接口
public class RealPrinter implements Printer {
    @Override
    public void printText(String text) {
        System.out.println("RealPrinter prints text: " + text);
    }

    @Override
    public void printImage(String image) {
        System.out.println("RealPrinter prints image: " + image);
    }
}
// 定义一个代理打印机类,持有真实打印机类的引用,并实现打印机接口
public class ProxyPrinter implements Printer {
    private Printer printer;

    public ProxyPrinter(Printer printer) {
        this.printer = printer;
    }

    @Override
    public void printText(String text) {
        System.out.println("ProxyPrinter starts printing text");
        printer.printText(text);
        System.out.println("ProxyPrinter finishes printing text");
    }

    @Override
    public void printImage(String image) {
        System.out.println("ProxyPrinter starts printing image");
        printer.printImage(image);
        System.out.println("ProxyPrinter finishes printing image");
    }
}

然后我们可以通过代理打印机对象来调用真实打印机对象的方法,并在前后添加日志信息:

public class Main {
    public static void main(String[] args) {
        Printer realPrinter = new RealPrinter();
        Printer proxyPrinter = new ProxyPrinter(realPrinter);
        proxyPrinter.printText("Hello World");
        proxyPrinter.printImage("Java.png");
    }
}

输出结果如下:

ProxyPrinter starts printing text
RealPrinter prints text: Hello World
ProxyPrinter finishes printing text
ProxyPrinter starts printing image
RealPrinter prints image: Java.png
ProxyPrinter finishes printing image

动态代理的实现方式与Kotlin也类似,但是需要使用Java提供的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。Proxy类可以在运行时动态地创建一个代理对象,该对象实现了指定的接口;InvocationHandler接口可以定义一个调用处理器,用于处理代理对象的方法调用,并在调用真实主题对象的方法前后添加额外的操作。例如:

public class Main {
    public static void main(String[] args) {
        Printer realPrinter = new RealPrinter();
        Printer proxyPrinter = (Printer) Proxy.newProxyInstance(
                realPrinter.getClass().getClassLoader(),
                realPrinter.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("ProxyPrinter starts printing " + method.getName());
                        Object returnValue = method.invoke(realPrinter, args);
                        System.out.println("ProxyPrinter finishes printing " + method.getName());
                        return returnValue;
                    }
                }
        );
        proxyPrinter.printText("Hello World");
        proxyPrinter.printImage("Java.png");
    }
}

输出结果与上面相同。

这种方式可以避免编写代理类,而是利用反射机制在运行时动态地创建代理对象,并根据需要添加额外的操作。但是这种方式也有一些缺点,例如性能开销较大,需要生成字节码并加载到内存中;只能针对接口进行代理,不能针对类进行代理。

上一篇下一篇

猜你喜欢

热点阅读