二十结构型-代理模式定义静态代理动态代理应用场景

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

image.png
其他设计模式介绍
创建型:
工厂方法
抽象工厂
原型
结构型:
适配器
桥接模式
组合模式
装饰模式
外观模式
享元模式
代理模式
行为型:
职责链
命令
解释器
迭代器
中介者
备忘录
状态模式
策略模式
模板方法
访问者

定义

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

静态代理

静态代理的模式在平时生活中也很常见,比如买火车票这件小事,黄牛相当于是火车站的代理,我们可以通过黄牛或者代售点进行买票行为,但只能去火车站进行改签和退票,因为只有火车站才有改签和退票的方法。

在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能(买票),并添加一些额外的业务处理(收取手续费),同时可以屏蔽realSubject中未开放的接口(改签和退票)。

image.png

//  委托类接口
 interface Subject {
    fun request()
}
//  具体委托类
 class RealSubject : Subject {
    override fun request() {
        println("RealSubject")
    }
    
    fun run(){}
}
// 代理类
class Proxy(private val subject: Subject) : Subject {
    override fun request() {
        println("begin")
        subject.request()
        println("end")
    }
}
fun main() {
    val subject = RealSubject()
    val p = Proxy(subject)
    p.request()
}
复制代码
  1. RealSubject 是委托类,Proxy 是代理类;
  2. Subject 是委托类和代理类的接口;
  3. request() 是委托类和代理类的共同方法;
  4. RealSubject 中有未公开的方法
  • 静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。

动态代理

  • 代理对象不需要实现目标对象的接口。
  • 代理对象的生成,使用的是Java的API,动态的在内存中构件代理对象(这需要我们指定创建代理对象/目标对象的接口的类型)。
  • 动态代理也叫做JDK代理、接口代理。
  • 相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,

下面通过一个例子看看如何实现jdk动态代理。

// 目标对象接口
interface IUserDao {
    fun save()
}
// 目标对象类
class UserDao : IUserDao {
    override fun save() {
        println("---------已经保存数据----------")
    }
}

/**
 * 创建动态代理对象
 * 动态代理对象不需要实现接口,但是需要指定接口类型
 */
class ProxyFactory(private val target: Any) {
    //给目标对象生成代理对象
    val proxyInstance: Any
        get() = Proxy.newProxyInstance(
            target.javaClass.classLoader, target.javaClass.interfaces
        ) { proxy, method, args ->
            println("Begin Transaction")
            //执行目标对象方法
            val returnValue = method.invoke(target, *args)
            println("Commit Transaction")
            returnValue
        }
}

fun main() {
    //目标对象
    val userDao: IUserDao = UserDao()
    //原始类型 class com.sschen.proxy.UserDao
    println(userDao.javaClass)

    //给定目标对象,动态创建代理对象
    val proxy = ProxyFactory(userDao).proxyInstance as IUserDao
    //代理对象类型 class com.sun.proxy.$Proxy0
    println(proxy.javaClass)
    proxy.save()
}
复制代码

从上面的代码可以看出,动态代理对象不需要实现目标对象接口,但是目标对象一定要实现接口,否则不能使用动态代理。

应用场景

说完对动态代理,下面来说一下代理模式的应用场景吧。
相信我们在开发的过程都会去请求网络,如果我们采用 Java 自带的网络框架去请求网络就会显得很臃肿,因此我们一般会选用一些网络请求框架进行封装,最后进行调用,整个框架如图:

image.png

在这种网络架构下,看起来很完美,对网络框架都进行了封装,当应用层需要请求网络时调用一下工具层的 post 方法,最后通过框架的具体方法去请求网络。

如果在这种架构下需要更换网络框架会发生什么问题呢?
虽然对每个框架都进行了封装,但是每个框架的入参以及其他返回参数等都不一样,这就导致了工具层的 post 方法需要做出对应的修改,同时应用层在调用的地方也需要做出相应的修改,这显然是不符合开闭原则的。

而代理模式就可以很好的解决这个问题,在上面这种架构下的工具层下面在加多一层代理层,也就是一个网络请求接口,而在工具层中的 post 方法也实现了这个接口,在构造方法中传入一个代理对象,而不同的框架也去实现这个接口,在实现方法中去做不同的请求方法,架构如下图:

image.png

代理层是一个接口(post),如果有多个框架,则每个框架都去实现该接口(post),在实现方法中做每个框架的不同的请求操作。

应用层也去实现代理层的接口,构造方法传入的代理层接口对象,在接口的实现方法中调用 传入的代理层接口对象 的post方法

这就印证了开闭原则