什么是代理模式
代理模式(Proxy Design Pattern)为另一个对象提供一个替身或占位符以控制对这个对象的访问。
理解代理模式
代理模式所要解决的问题其实是要控制对某一个对象的访问,这句话怎么理解呢?就好比生活中我们需要去做某些事情,比如买房,出国旅游等,由于我们对买房,出国旅游的相关办理手续不是很熟悉,所以我们希望找一个人帮助我们去解决这个事情。委托他代理我们去找相关的机构办理相关的业务。注意,这个时候机构直接面对的对象是我们委托的人,而不是我们本人。虽然很多事情本来是要我们自己完成的,但是代理替我们更加好地完成了。
一些不同的代理控制
- 远程代理控制访问远程对象
- 虚拟代理控制访问创建开销大的资源
- 保护代理基于权限控制对资源的访问
代理模式类图
下面分别介绍类图中各个组件的作用:
Proxy
和RealSubject
都实现了Subject
接口,这允许任何客户都可以像处理RealSubject
对象一样来处理Proxy
对象。
Proxy
持有Subject
的引用,所以必要时它可以将请求转发给Subject
。
RealSubject
就是真实对象
啦,也就是实际做事的那个对象,Proxy
会控制对RealSubject
的访问。
在某些例子中,Proxy
还会负责RealSubject
对象的创建和销毁。客户和RealSubject
的交互都必须通过Proxy
。因为Proxy
和RealSubject
实现了想用的接口(Subject
),所以任何用到RealSubject
的地方都可以用Proxy
取代。Proxy
也控制了对RealSubject
的访问,在某些情况下,我们可能需要这样的控制。这些情况包括RealSubject
是远程的对象,RealSubject
创建开销大,或者是RealSubject
需要被保护。
实例分析-CD封面虚拟代理
我们知道使用CD播放机来播放CD的时候我们可以看到每一首歌曲的封面,其实这里面也是使用到了代理模式。现在其实播放CD的很少了,不过很久以前非常流行,那个时候电脑内存小,磁盘速度慢,因此打开一张图需要缓冲一段时间,因此需要在加载的时候给用户提供一个不错的提示,能让他“耐心”地等一会儿。
先展示这个CD封面加载模式的类图结构吧:
简单说明一下系统的逻辑,我们可以看到,代理ProxyImage
比真实对象RealImage
少了一个方法loadFromDisk()
。这个非常重要,说明代理对象最终的目的是为了控制这个对象的访问而不是改变或者新增这个对象的行为。
创建一个Image接口:
1 | package com.qinjiangbo.proxy; |
然后先实现RealImage类:
1 | package com.qinjiangbo.proxy; |
再实现ProxyImage
类:
1 | package com.qinjiangbo.proxy; |
实例测试
写一个带有main函数的类进行测试:
1 | package com.qinjiangbo.proxy; |
结果如下:
1 | Loading yesterday-once-more.png |
装饰者模式 vs 代理模式
其实这个两个模式非常容易被人搞混,为什么呢?因为这两个模式在结构上几乎没什么区别。我们再来回顾一下装饰者模式的类图:
可以看到装饰者模式和代理模式都是需要实现一个公共的接口,不管是装饰者与被装饰者还是代理者与被代理者。当然还是有很多不一样的地方的,我们细细道来:
- 代理模式中只是对真实对象的“代表”,而不是“装饰”对象。
- 代理模式和装饰者模式虽然结构很像,但是目的意图完全不同。
- 比较重要的一点是代理模式能够实例化它代理的对象,而装饰者模式不行,它只能从外部传入进来。
远程方法调用
Java中的远程方法调用调用就是代理模式的典型代表,我们可以看一下这个过程:
- 客户端调用某个对象的方法,代理接收到这个请求以后将相关方法、参数等信息通过序列化的方式发送给服务端,服务端收到以后完成本地方法调用。
- 服务端调用完成以后,返回给本地代理,代理奖将结果进行序列化传递给客户端,客户端收到结果,就像在本地一样,完全无感知。
总结
代理模式非常的重要,以至于我后面还会重点写一篇文章来介绍代理模式在Java中的几种典型代表。它在我们的生产中有着非常重要的作用,比如我们最常见的Dubbo,HSF等RPC框架,都是基于代理模式来实现的。掌握好代理模式,我们也能自己实现一个RPC框架,这对于我们理解网络,代理,反射等各种技术要点理解都是有非常重要的提升和锻炼作用的。