代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:
代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户 端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需 要的额外的新服务。
代理模式的通用类图如图12-1所示:

根据类图,代理模式包含三个角色:
● Subject:抽象主题角色,它声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
● RealSubject:具体主题角色也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
● Proxy:代理主题角色,也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制 委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。
来看看具体的代码实现。
- Subject:抽象主题类
抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类。
- RealSubject:真实主题类
真实主题类继承了抽象主题类,提供了业务方法的具体实现
- Proxy:代理类
代理类也是抽象主题类的子类,它对真实主题对象引用,调用在真实主题中实现的业务方法,在调用时可以在原有业务方法的基础上附加一些新的方法来对功能进行扩充或约束
在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种。
在网络上代理服务器设置分为透明代理和普通代理:
- 透明代理就是用户 不用设置代理服务器地址,就可以直接访问,也就是说代理服务器对用户来说是透明的,不 用知道它存在的;
- 普通代理则是需要用户自己设置代理服务器的IP地址,用户必须知道代理 的存在。
设计模式中的普通代理和强制代理也是类似的一种结构,普通代理就是我们要知道代理的存在,然后才能访问;强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生是由真实角色决定。
以代练代打游戏举例说明,普通代理,它的要求就是客户端只能访问代理角色,而不能访问真实角色,这是比较简单的。以代练打游戏升级的例子作为扩展,游戏玩家,自己不练级 ,场景类不直接new一个GamePlayer对象了,由GamePlayerProxy来进行模拟场景。

具体代码如下:
- IGamePlayer:接口
- GamePlayer:游戏者
在构造函数中,传递进来一个IGamePlayer对象,检查谁能创建真实的角色
- GamePlayerProxy:代理者
仅仅修改了构造函数,传递进来一个代理者名称,即可进行代理,在这种改造下,系统 更加简洁了,调用者只知道代理存在就可以,不用知道代理了谁。
- Client:场景类
在代理中,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有任何的影响,只要实现了接口所对应的方法,该模式非常适合对扩展性要求较高的场合。当然,在实际的项目中,一般都是通过约定来禁止new一个真实的角色,这也是一个 非常好的方案。
强制代理在设计模式中比较另类,一般的思维都是通过代理找到真实 的角色,但是强制代理却是要“强制”,必须通过真实角色查找到代理角色,否则你不能访 问。不管是通过代理类还是通过直接new一个主题角色类,都不能访问,只有通过真实角色指定的代理类才可以访问,也就是说由真实角色管理代理角色。

代码如下:
- IGamePlayer:接口
在接口上增加了一个getProxy方法,真实角色GamePlayer可以指定一个自己的代理,除 了代理外谁都不能访问。
- GamePlayer:强制代理的真实角色
增加了一个私有方法,检查是否是自己指定的代理,是指定的代理则允许访问,否则不允许访问。
- GamePlayerProxy:强制代理的代理类
- Client:场景类
强制代理的概念就是要从真实角色查找到代理角色,不允 许直接访问真实角色。高层模块只要调用getProxy就可以访问真实角色的所有方法,它根本 就不需要产生一个代理出来,代理的管理已经由真实角色自己完成。
一个类可以实现多个接口,完成不同任务的整合。也就是说代理类不仅仅可以实现主题 接口,也可以实现其他接口完成不同的任务,而且代理的目的是在目标对象方法的基础上作增强,这种增强的本质通常就是对目标对象的方法进行拦截和过滤。例如游戏代理是需要收费的,升一级需要5元钱,这个计算功能就是代理类的个性,它应该在代理的接口中定义, 如图12-3所示:

增加了一个IProxy接口,其作用是计算代理的费用。
GamePlayerProxy类实现该接口:
同时在upgrade方法中调用该方法,完成费用结算。
什么是动态代理?动态代理是在实现阶段不用关心代理谁,而在运行阶段才指定代理哪一个对象。
相对来说,自己写代理类的方式就是静态代理。面向横切面编程,也就是AOP(Aspect Oriented Programming),其核心就是采用了动态代理机制。
动态代理实例
还是以打游戏为例,类图修改一下以实现动态代理,如图12-4所示:

在类图中增加了一个InvocationHandler接口和GamePlayIH类,作用就是产生一个对象的代理对象,其中InvocationHandler是JDK提供的动态代理接口,对被代理类的方法进行代理。 我们来看程序,接口保持不变,实现类也没有变化。
- GamePlayIH:动态代理类
其中invoke方法是接口InvocationHandler定义必须实现的,它完成对真实方法的调用。
InvocationHandler接口——动态代理是根据被代理的接口生成所有的方法, 也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,通过 InvocationHandler接口,所有方法都由该Handler来进行处理,即所有被代理的方法都由InvocationHandler接管实际的处理任务。
*Client: 场景类
运行结果:

动态代理详解
在上面的动态代理类里,游戏登录时会通知。
这就是AOP编程。AOP编程没有使用什么新的技术,但是它对设计、编码有非常大的影响,对于日志、事务、权限等都可以在系统设计阶段不用考虑,而在设计后通 过AOP的方式切过去。
通用动态代理模型,类图如图 12-5所示:

动态代理实现代理的职责,业务逻辑Subject实现相关的 逻辑功能,两者之间没有必然的相互耦合的关系。通知Advice从另一个切面切入,最终在高层模块也就是Client进行耦合,完成逻辑的封装任务。
具体代码实现如下:
- Subject:抽象主题
- RealSubject:真实主题
- MyInvocationHandler:动态代理的Handler类
所有通过动态代理实现的方法全部通过invoke方法调
- DynamicProxy:动态代理类
- 通知接口及实现
- 动态代理的场景类:
运行结果:

看看程序是怎么实现的。在DynamicProxy类 中,有这样的方法:
该方法是重新生成了一个对象。注意 c.getInterfaces(),查找到该类的所有接口,然后实现接口的所有方法。当然了,方法都是空的,由谁具体负责接管呢?是new MyInvocationHandler(_Obj)这个对象。于是就知道一个类的动态代理类是这样的一个类, 由InvocationHandler的实现类实现所有的方法,由其invoke方法接管所有方法的实现,其动态调用过程如图12-6所示。

以上的代码还有更进一步的扩展余地,DynamicProxy类,它 是一个通用类,不具有业务意义,可以再产生一个实现类:
如此扩展以后,高层模块对代理的访问会更加简单:
代理模式优点
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。
代理模式缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求 的处理速度变慢,例如保护代理。
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。
⇐⇐ 设计模式—— 十一 :建造者模式
⇒⇒ 设计模式—— 十三 :原型模式
参考:
【1】:《设计模式之禅》
【2】:《design pattern java》
【3】:《研磨设计模式》
【4】:Java Review(三十九、类加载机制与反射)
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/10439.html