博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式之代理模式
阅读量:6236 次
发布时间:2019-06-22

本文共 3655 字,大约阅读时间需要 12 分钟。

前言

最近在研究Retrofit的原理,其中核心的部分是通过动态代理来实现的,虽然之前了解过,但对于这个设计模式,一直处于懵懂状态,从来没用过,就着这个机会,又深入研究了一下。代理模式类似于现实生活中的委托,实现方式上可以分为静态代理和动态代理。上图是代理模式UML图。

为什么要使用代理模式?

对于熟悉这个模式的人来说,代理模式实现起来非常简单,那你可能会问,为什么要使用代理模式,或者说使用代理有什么好处呢?下面我们一一来分析

  • 实际执行过程中,额外做一些别的事情

    举个例子:果农在自家生产水果,由于人力物力等原因,不能零售,所以果农把水果卖给水果商贩,由商贩在全国各地售卖。这里果农是被代理者,水果商贩是代理者;商贩在售卖前,为了吸引人购买,需要对水果做一些处理,如对水果进行清洗,打蜡,削皮等等;在卖出水果之后,商贩还可以根据售卖情况进行汇总,选出热销水果,方便后面多进些货等操作

  • 用户和被代理对象隔离,被代理对象只需关注自身业务,可以实现复用

    还拿上面的例子来说,用户只和水果商贩打交道,不和果农产生直接联系,体现了隔离;果农只负责种水果和卖给水果商贩,业务比较专;而且一个果农可以很多个水果商贩打交道,体现了果农的复用特性

以上分析和举例只是为了说明代理的优势,不得当之处还请见谅。

静态代理 vs 动态代理

前面提到了代理分为静态代理和动态代理,这两种代理只是在java语言特性上创建代理类的不同实现方式,实际产生的效果是相同的,所以上面分析的优势仍然适用二者。下面,来看看二者的区别吧

类别 作用阶段 需实现抽象方法个数 代理执行方式
静态代理 编码阶段 all 单独代理,一一对应
动态代理 运行阶段 0 统一管理
  • 静态代理:在编码阶段创建代理类,直接使用该代理类型的对象;由于代理类需要继承被代理类的抽象,所以需要实现所有的抽象方法,抽象方法很多的情况下,管理起来就变的非常繁琐。
  • 动态代理:在运行期间,自动在内存中创建一个代理对象,不需要创建具体的代理类型;通过实现InvocationHandler接口,直接实现委托对象方法的调用,InvocationHandler的invoke()方法,代理了委托对象的所有方法,即委托对象的每一个方法调用,都会调用invoke()方法,也因此导致invoke()方法实现起来比较复杂。

静态代理

以上都是代理模式的理论,下面是时候来展示一下它们的神奇了。以上面举的例子为基础,看看编码是如何实现的。实现静态代理,主要有以下4步(从UML就可以看出来了哦)

  1. 创建委托类的抽象
/** * @Description: 抽象类-卖水果 */interface IFrultSell {    fun sellFrult(frultName: String): Unit}复制代码
  1. 创建具体委托类,可能有多个
/** * @Description: 被代理类-果农 */class FrultGrower : IFrultSell {    override fun sellFrult(frultName: String) {        println("我是果农,出售自己种植的$frultName")    }}复制代码
  1. 创建代理类
/** * @date: 2019/5/10 * @author: zzf * @Description: 代理类-水果商贩 */class ProxyProduct(var applePro: IFrultSell?) : IFrultSell {    override fun sellFrult(frultName: String) {        println("出售精品$frultName,干净无农药")        applePro!!.sellFrult(frultName)        println("统计卖出的数量")    }}复制代码
  1. 做完了所有的准备工作,接下来就是使用了
/** * @Description: 消费者 */object Consumer {    @JvmStatic    fun main(args: Array
): Unit { //创建果农对象 val fruPro: IFrultSell = FrultGrower() //创建代理对象 val frultSeller = ProxyProduct(fruPro) val banana = Frults.BANANA //用户调用 frultSeller.sellFrult(frultName = banana.getName()) }}复制代码

以上就是静态代理完整的实现过程,注释很详细,就不做过多的解释了,下图是执行的过程

动态代理

介绍完了静态代理,剩下的就是动态代理的实现了,且听我娓娓道来,哈哈。 还是以上面的例子为基础,动态代理的实现共4步,分别是

  1. 创建委托类的抽象
/** * @Description: 抽象接口-卖水果 */interface IFrultSell {    /**     * 卖水果     */    fun sailFrult(name: String): Unit}复制代码
  1. 创建具体委托类,可能有多个
/** * @Description:被代理类-果农 */class FrultGrower() : IFrultSell {    override fun sailFrult(name: String) {        println("我是果农,出售自己种植的$name")    }}复制代码
  1. 创建动态代理执行器,此处开始区别于静态代理
/** * @Description: 动态代理的InvocationHandler */class FrultHandler(var iFrult: IFrultSell) : InvocationHandler {    override fun invoke(proxy: Any?, method: Method?, args: Array
?): Any? { if (method!!.name.equals("sailFrult",false)){ //代理的是卖水果的操作,可以在此进行额外操作 println("大家快来看啊,我这里有${args!![0]}出售") } val result = method.run { invoke(iFrult, *args!!) } //代理执行完毕,可以在此执行额外操作 println("代理执行操作完毕~ ~") return result }}//invoke()作为代理的公共方法,代理所有委托方法的实现复制代码
  1. 运行时创建代理并使用
/** * @Description: */object Consumer {    @JvmStatic    fun main(args: Array
): Unit { //声明被代理者(果农) val seller: IFrultSell = FrultGrower() //声明代理的处理者 val handler = FrultHandler(seller) //创建代理对象 val iFrult = Proxy.newProxyInstance(IFrultSell::class.java.classLoader, arrayOf(IFrultSell::class.java), handler) as IFrultSell //用户调用 iFrult.sailFrult("苹果") }}复制代码

如上所示,通过Proxy.newProxyInstance()方法创建一个代理对象,InvocationHandler对象作为参数,当调用代理对象的方法(如iFrult.sailFrult())时,会回调InvocationHandler.invoke()方法,执行我们预设的逻辑。所以,动态代理的实现流程还是很简单的,而要说复杂的地方,是在代理执行器InvocationHandler.invoke()的处理上,因为此处需要通过反射实现,具体的实现就要根据自己的业务而定了。老样子,下图是动态代理的执行结果,和静态代理的结果相同

转载地址:http://jgkia.baihongyu.com/

你可能感兴趣的文章
移动H5前端性能优化指南
查看>>
报表制作工具中自定义函数概述
查看>>
Sqoop2从Mysql导入Hdfs (hadoop-2.7.1,Sqoop 1.99.6)
查看>>
浮点数指令
查看>>
无法删除文件名称过长的文件
查看>>
手机端页面流畅滚动
查看>>
CentOS下 CPU 负载观察和性能监测
查看>>
Magento产品页面包屑导航(Breadcrumb)修正
查看>>
struts2 多文件上传
查看>>
在样式中控制列表长度
查看>>
项目经理之项目经理应该做什么(转)
查看>>
Git 分支 - 分支的衍合
查看>>
ubuntu在vmware下的安装与配置
查看>>
codewars050: 丢失的数组的长度
查看>>
JavaScript获取元素在浏览器画布中的绝对位置【转】
查看>>
程序员小说《OutOfMemory》第三次更新的部分
查看>>
interface和abstract interface
查看>>
Android之rild进程启动源码分析
查看>>
TextView
查看>>
Sql入门视频教程荟萃
查看>>