Kotlin-基础-01-入门-2 代理模式

151 阅读5分钟

1. 静态代理

在 Kotlin 中,静态代理的实现方式与 Java 类似。静态代理通过手动创建代理类来实现,这个代理类持有目标对象的引用,并在方法调用时为目标对象添加额外的逻辑。静态代理的核心思想是编写一个代理类,该类和目标对象实现相同的接口,并在代理类中对目标对象的行为进行增强。

1.1. 定义接口和目标类

首先,定义一个接口,并创建一个具体的实现类(即目标对象),该类包含业务逻辑。

kotlin
复制代码
// 定义接口
interface Service {
    fun execute()
}

// 目标对象
class RealService : Service {
    override fun execute() {
        println("Executing service...")
    }
}

1.2. 创建静态代理类

接下来,我们手动创建一个代理类,这个代理类实现了和目标对象相同的接口,并持有目标对象的引用。在代理类的方法中,可以对目标对象的方法调用进行增强,例如在方法前后添加日志、权限控制等。

kotlin
复制代码
// 静态代理类
class ServiceProxy(private val realService: Service) : Service {

    override fun execute() {
        println("Before execution...") // 在方法前执行的逻辑
        realService.execute()           // 调用目标对象的方法
        println("After execution...")  // 在方法后执行的逻辑
    }
}

1.3. 使用静态代理类

然后,在客户端代码中创建目标对象和代理对象,并通过代理对象调用方法。

kotlin
复制代码
fun main() {
    // 创建目标对象
    val realService = RealService()

    // 创建代理对象
    val serviceProxy = ServiceProxy(realService)

    // 通过代理对象调用方法
    serviceProxy.execute()
}

1.4. 运行结果

当你运行上述代码时,输出将是:

mathematica
复制代码
Before execution...
Executing service...
After execution...

1.5. 解释

  • 接口和目标类Service 接口定义了一个方法 execute(),目标类 RealService 实现了这个接口,并定义了具体的业务逻辑。
  • 代理类ServiceProxy 是手动创建的代理类,它实现了 Service 接口,并持有一个 Service 类型的对象。通过代理类,我们可以在调用目标对象的方法之前和之后执行额外的逻辑。
  • 增强功能:在代理类的 execute() 方法中,我们添加了在调用目标方法前后执行的代码,实现了对目标方法的增强。

1.6. 静态代理的优缺点

  • 优点

    • 通过代理类可以在不修改目标对象的前提下增加额外的功能。
    • 适用于一些简单的增强场景,例如日志记录、权限检查等。
  • 缺点

    • 每一个目标类都需要对应一个代理类,代码量增多,维护成本较高。
    • 如果接口发生变化,代理类也需要进行相应的修改,耦合度较高。

1.7. 总结

在 Kotlin 中,静态代理的实现方式与 Java 几乎相同。通过手动创建代理类并在其中增强目标对象的行为,开发者可以在保持目标对象不变的前提下实现功能的扩展。虽然静态代理的实现简单直观,但在复杂场景下可能会增加代码的冗余性和维护成本,因此在实际开发中,静态代理通常用于简单的场景。 在 Kotlin 中,你可以通过使用与 Java 类似的方式来实现动态代理。Kotlin 也提供了对 Java 动态代理机制的支持,这意味着你可以使用 java.lang.reflect.ProxyInvocationHandler 来实现动态代理。以下是 Kotlin 实现动态代理的示例:

2. 动态代理

2.1. 定义接口和目标类

首先,我们定义一个接口和它的实现类(即目标类),这个类包含了业务逻辑。

kotlin
复制代码
// 定义接口
interface Service {
    fun execute()
}

// 目标对象
class RealService : Service {
    override fun execute() {
        println("Executing service...")
    }
}

2.2. 创建 InvocationHandler

然后,我们需要创建一个 InvocationHandler 实现类,它定义了代理对象在调用方法时的行为。这个类将在方法执行前后添加一些自定义逻辑。

kotlin
复制代码
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method

class ServiceInvocationHandler(private val target: Any) : InvocationHandler {
    override fun invoke(proxy: Any, method: Method, args: Array<Any>?): Any? {
        println("Before execution...")
        val result = method.invoke(target, *(args ?: arrayOfNulls<Any>(0)))
        println("After execution...")
        return result
    }
}

2.3. 创建代理对象

接下来,我们使用 Proxy.newProxyInstance 方法来创建代理对象。

kotlin
复制代码
import java.lang.reflect.Proxy

fun main() {
    val realService = RealService()

    // 创建代理对象
    val serviceProxy = Proxy.newProxyInstance(
        realService::class.java.classLoader,
        arrayOf(Service::class.java),
        ServiceInvocationHandler(realService)
    ) as Service

    // 调用代理对象的方法
    serviceProxy.execute()
}

2.4. 运行结果

当你运行上述代码时,输出将是:

mathematica
复制代码
Before execution...
Executing service...
After execution...

2.5. 解释

  • 接口和目标对象Service 接口定义了一个方法 execute,目标类 RealService 实现了这个接口。
  • InvocationHandlerServiceInvocationHandler 实现了 InvocationHandler 接口,在 invoke 方法中,可以在目标方法执行前后插入自定义逻辑。
  • 动态代理:通过 Proxy.newProxyInstance 方法动态生成了一个代理对象,这个对象实现了 Service 接口,并将方法调用委托给 ServiceInvocationHandler 处理。

2.6. 使用 Kotlin 的 lambda 表达式改写

由于 Kotlin 的函数式特性,你也可以使用更简洁的方式来实现代理。例如,你可以将 InvocationHandler 的实现直接写成 lambda 表达式:

kotlin
复制代码
fun main() {
    val realService = RealService()

    // 使用 lambda 表达式创建代理对象
    val serviceProxy = Proxy.newProxyInstance(
        realService::class.java.classLoader,
        arrayOf(Service::class.java)
    ) { _, method, args ->
        println("Before execution...")
        val result = method.invoke(realService, *(args ?: arrayOfNulls<Any>(0)))
        println("After execution...")
        result
    } as Service

    // 调用代理对象的方法
    serviceProxy.execute()
}

这个版本使用了更少的代码来实现相同的功能,同时充分利用了 Kotlin 的语法优势。

2.7. 实际应用

Kotlin 动态代理在实际应用中可以用于实现类似 AOP 的功能,如日志记录、权限控制、事务管理等。当你需要在不修改目标对象代码的情况下添加或修改功能时,动态代理是一个非常强大的工具。