设计模式-代理模式(Proxy Model)
文 / vincentzh
原文连接:http://www.cnblogs.com/vincentzh/p/5988145.html
目录
1、写在前面
这次写代理模式,代理模式里面的动态代理(Dynamic Proxy)逻辑稍显混乱,不太容易理解,本章节会将代理模式里面的静态代理(Static Proxy)和动态代理(Dynamic Proxy)结合对比理解,其中动态代理的实现需要具备 Java 的反射(Reflection)知识,原理方面会结合实例和静态代理的对比尽量说清楚。
2、概述
在某些情况下,一个客户不想或者不能直接引用一个对象,代理对象就再客户端和被代理对象之间起到中介的作用。就好比你在北京租房,初来乍到,人生地不熟,找房子遍地都是中介,想找房东可没那么容易(基本算得上是找不到房东直租的)。问题来了,找不到房东直租,但房子又是一切的基础,so....走中介,房东能租房,中介也能租房,不过是你通过中介去将房东的房子租给自己。OK,这就是一个活生生的代理模式的例子,相必在外漂泊的小年轻们都感同身受吧。
再说说代理模式的分类,代理模式主要分为两类:静态代理(Static Proxy)、动态代理(Dynamic Proxy)。
静态代理:一个被代理的真实对象对应一个代理,相当于一个租房中介只代理一个房东。弊端很明显,这样下去中介早饿死了!!!并且中介公司管理这么庞大的中介团队早晚逗得垮掉。反应到代码里面就是:代理类急剧增加,灵活性降低,增加了代码的复杂度。
动态代理:动态代理主要是去解决静态代理存在的问题(及一个代理对应一个被代理对象),现实中也是这样,不可能有中介只做一个房东的生意,一个中介手里n多房子供你选择,3人间、6人间、隔断、小两居等各种房源。反应到代码里面:代理类只有一个,根据客户需求动态的去改变代理的真实对象,增加了代码的灵活性,降低了代码的复杂性。
3、目的
代理模式的目的在于:为其他对象提供一种代理的方式控制被代理对象。
4、结构组成
代理模式主要涉及到三个角色:抽象角色、代理角色、真实角色(被代理的角色)。
抽象角色:声明真实对象和代理对象的共同接口。即真实对象和代理对象共同要实现的行为动作(好比房东和中介都要能够实现租房的行为,都能把房子租给你)。
代理角色:代理角色内部含有对真实角色的引用,从而可以去操作真实对象,同时代理对象提供与真实对象的接口,以便在任何时候都能代替真实对象。同时,代理对象在执行真实对象的操作时,也能附加它自己的操作,相当于对真实对象的封装(可以理解为中介在执行将房东的房子租给你这一操作时,可以向你收取中介费,还可以在退房的时候扣你押金这类房东不具有的操作)。
真实角色:代理角色所代理的对象,亦即我们最终要引用的对象。
5、实现
5.1 静态代理
静态代理主要涉及到的三个角色关系图如下:
代理角色和真实角色都要实现同一个操作行为接口(或继承同一个抽象类),并且代理角色依赖于真实角色,因为代理角色内部有对真实角色的引用,代理在操作真实角色去执行动作时,必须要知道是哪 个真实角色。
下面是静态代理的实现:
声明抽象角色和代理角色实现的共同接口。
package com.cnblogs.vincentzh.staticproxy;
//声明出租房屋接口
public interface RentOut
{
public void rentOut();
}
创建真实角色,实现租房接口。
package com.cnblogs.vincentzh.staticproxy;
// 创建房主类(真实角色)
public class HouseHolder implements RentOut
{
@Override
public void rent()
{
System.out.println("I’m renting the house!");
}
}
创建代理角色,实现与真实角色相同的接口
package com.cnblogs.vincentzh.staticproxy;
// 创建代理(中介)角色,与房东实现相同接口
public class Proxy implements RentOut
{
private HouseHolder houseHolder; // 代理角色内部含有真实角色的引用 // 重写租房方法,添加中介操作
@Override
public void rent()
{
this.preRentOut(); // 代理对象添加自己的操作 if(null == houseHolder)
{
houseHolder = new HouseHolder();
}
houseHolder.rent(); this.postRentOut(); // 代理对象添加自己的操作
} // 中介操作,租房前收中介费
public void preRentOut()
{
System.out.println("I need more money!");
} // 中介操作,租房后扣押金
public void postRentOut()
{
System.out.println("I will deduct some money!");
}
}
创建真实环境类,实现代理功能。
客户对象并不知道被代理的真实对象,只知道自己需要进行某项操作,并且某个代理能帮助自己完成这一操作。(即客户要租房子,并不知道要从哪个房东手里租房,只知道中介能帮助自己租到房子,至于房子从哪个房东那里租的,就是中介(代理)的事儿了,客户并不知道,也不需要直到)。
package com.cnblogs.vincentzh.staticproxy;
// 租房客户类
public class Client
{
/**
* @param args
*/
public static void main(String[] args)
{
// 涉世未深的小年轻要租房,苦于联系不到房东,只能通过中介
RentOut rentOut = new Proxy();
rentOut.rent();
}
}
运行程序,客户类通过代理访问真实角色,不仅实现真实角色的操作行为,同时也添加了代理自己的操作行为。
静态代理的弊端就在于:每当有一个真实对象需要被代理,就需要创建一个新的代理类,因为代理类内含有对真实对象的引用,代理类需要与真实对象一一对应。这样,当需要代理的真实对象比较多时,代理类就会急剧增加,增加了代码的复杂性,使代码变得尤其繁重,不易维护。那么动态代理就很好的解决了这一问题。
5.2 动态代理
动态代理解决了静态代理存在的弊端,实现了多个被代理真实对象仅由一个代理类代理即可。
1)创建两个行为接口,分别实现 rent、sale 不同操作的行为;
package com.cnblogs.vincentzh.dynamicproxy;
// 创建租房接口
public interface Rent
{
public void rent();
}
package com.cnblogs.vincentzh.dynamicproxy;
// 创建售卖接口
public interface Sale
{
public void sale();
}
2)创建两个需要被代理的真实对象类,且他们分别实现了含有不同操作行为的接口;
package com.cnblogs.vincentzh.dynamicproxy;
// 创建房主类(真实角色),实现租房接口
public class HouseHolder implements Rent
{
@Override
public void rent()
{
System.out.println("HouseHolder: I’m renting the house!");
}
}
package com.cnblogs.vincentzh.dynamicproxy;
// 创建商人类(真实对象),实现售卖接口
public class BusinessMan implements Sale
{
@Override
public void sale()
{
System.out.println("BusinessMan: I'm salling something!");
}
}
3)创建动态代理类,只需要一个代理类,就能够实现对 n 个需代理对象的代理,从而解决了静态代理中代理与真实对象一一对应导致类的数目急剧增加的问题。
package com.cnblogs.vincentzh.dynamicproxy;
// 创建动态代理类,实现代码运行过程中对各种真实对象的动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class DynamicProxy implements InvocationHandler
{
private Object realSubject; public DynamicProxy(Object realSubject)
{
this.realSubject = realSubject;
} public void setRealSubject(Object realSubject)
{
this.realSubject = realSubject;
} @Override
// 实现InvocationHandler接口的 invoke 方法,当代理类调用真实对象的方法时,
// 将直接寻找执行 invoke 方法。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
this.preRent(); // 执行代理自己添加的行为操作 method.invoke(realSubject, args); // 以反射(reflection)的形式引用真实对象的方法 this.postRent(); // 执行代理自己添加的行为操作
return null;
} // 代理类自行添加的行为
public void preRent()
{
System.out.println("I need more money!");
} // 代理类自行添加的行为
public void postRent()
{
System.out.println("I will deduct some money!");
}
}
4)客户调用类,只需通过改变被代理的真实对象,就能直接实现被代理的对象的更换,就不需要再去为 BusinessMan 类创建一个新的代理类了。BusinessMan类和HouseHolder类公用一个DynamicProxy动态代理类,但在做代理时,却是动态生成两个不同的代理实例(Proxy Instance),这就是所谓的动态代理。
package com.cnblogs.vincentzh.dynamicproxy;
//创建客户类
import java.lang.reflect.Proxy; public class Client
{
/**
* @param args
*/
public static void main(String[] args)
{
HouseHolder houseHolder = new HouseHolder();
DynamicProxy handler = new DynamicProxy(houseHolder); // 生成 HouseHolder 的代理 // 动态生成代理实例(HouseHold代理实例),代理支持的接口由初始化参数(第二个)指定,代理实例处理操作所调用的 handler 由第三个参数指定
Rent rent = (Rent)Proxy.newProxyInstance(HouseHolder.class.getClassLoader(), HouseHolder.class.getInterfaces(), handler);
rent.rent(); // 执行客户需要进行的行为操作,动态生成的代理实例直接调用指定 handler 的 invoke 方法 System.out.println("----------------------------------"); BusinessMan businessMan = new BusinessMan();
handler.setRealSubject(businessMan); // 为代理更换引用的真实对象(即原本被代理的 HouseHolder 被更换为了 BusinessMan)
// 动态生成代理实例(BusinessMan代理实例)
// 注:代理实例实在代码执行过程中动态执行的
Sale sale = (Sale)Proxy.newProxyInstance(BusinessMan.class.getClassLoader(), BusinessMan.class.getInterfaces(), handler);
sale.sale();
}
}
执行结果:
6、总结
代理模式解决了不能直接操作真实对象的问题,其中的动态代理更是使代理模式的使用简化不少。若使用静态代理,那么当有不用的真实对象(分别实现了不同的操作接口),我们只能去为被代理的真实对象重新生成一个代理类,而动态代理就解决了这一问题,用一个代理类,解决了所有真实对象的代理。通俗点将,这是个全能的代理,既能干租房中介的活,还能干买菜中介的活,也能干媒人的活,听起来貌似很牛逼的样子。
设计模式-代理模式(Proxy Model)的更多相关文章
- 设计模式 - 代理模式(proxy pattern) 未使用代理模式 具体解释
代理模式(proxy pattern) 未使用代理模式 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 部分代码參考: http://blog.csdn. ...
- 设计模式——代理模式(Proxy Pattern)
代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问. UML图: 模型设计: Subject类: package com.cnblog.clarck; /** * Subject 类 ...
- 设计模式--代理模式Proxy(结构型)
一.代理模式 为其他对象提供一种代理以控制对这个对象的访问. 代理模式分为四种: 远程代理:为了一个对象在不同的地址空间提供局部代表.这样可以隐藏一个对象存在于不同地址空间的事实. 虚拟代理:根据需要 ...
- C#设计模式——代理模式(Proxy Pattern)
一.概述在软件开发中,有些对象由于创建成本高.访问时需要与其它进程交互等原因,直接访问会造成系统速度慢.复杂度增大等问题.这时可以使用代理模式,给系统增加一层间接层,通过间接层访问对象,从而达到隐藏系 ...
- 设计模式-代理模式(Proxy)
应用场景: 领导都有秘书,一般会代理领导的部分职能角色,处理签字.报销.开会等任务.很多新人可能都只知道秘书的存在,毕竟每天与其打交道,不知道领导的存在.但是领导的的确确是真实存在的. 场景说明: 代 ...
- 大话设计模式--代理模式 proxy
1. 代理模式: 为其他对象提供一种代理以控制这个对象的访问. 代理模式使用场合: a. 远程代理, 为一个对象在不同的地址空间提供局部代理,隐藏一个对象存在于不同地址空间的事实.如.net中WebS ...
- 23种设计模式--代理模式-Proxy
一.代理模式的介绍 代理模式我们脑袋里出现第一个词语就是代购,其实就是这样通过一个中间层这个中间成是属于什么都干什么都买得,俗称"百晓生",在平时得开发中我们经常会听到 ...
- 设计模式 -- 代理模式 (Proxy Pattern)
定义: 为其他对象提供一种代理以控制对这个对象的访问: 角色: 1,抽象主题类,(接口或者抽象类),抽象真实主题和代理的共有方法(如下Subject类): 2,具体实现的主题类,继承或者实现抽象主题类 ...
- 深入浅出设计模式——代理模式(Proxy Pattern)
模式动机在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用.代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到 ...
- 【转】设计模式(十一)代理模式Proxy(结构型)
设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ...
随机推荐
- jQuery可拖拽3D万花筒旋转特效
这是一个使用了CSS3立体效果的强大特效,本特效使用jQuery跟CSS3 transform来实现在用户鼠标按下拖动时,环形图片墙可以跟随鼠标进行3D旋转动画. 效果体验:http://hovert ...
- 烂泥:jira7.2安装、中文及破解
. jira的主要配置文件,存放在/opt/atlassian/jira/conf/server.xml文件中,如下: vim /opt/atlassian/jira/conf/server.xml ...
- ENode框架Conference案例分析系列之 - 文章索引
ENode框架Conference案例分析系列之 - 业务简介 ENode框架Conference案例分析系列之 - 上下文划分和领域建模 ENode框架Conference案例分析系列之 - 架构设 ...
- C#的泛型的类型参数可以有带参数的构造函数的约束方式吗?
Review后看到标题让我十分羞愧自己语文功底太差,估计...请见谅......我还特地把这句写回开头了...... 问题 前天遇到的一个问题,所以在MSDN发了个问,刚也丰富了下问题,关于泛型的. ...
- Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高
问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...
- 深入MySQL索引
MySQL索引作为数据库优化的常用手段之一在项目优化中经常会被用到, 但是如何建立高效索引,有效的使用索引以及索引优化的背后到底是什么原理?这次我们深入数据库索引,从索引的数据结构开始说起. 索引原理 ...
- 透视 HTML子元素的margin-top样式会应用在父元素上的原由
情况说明 当对页面中元素设置margin-top样式时,如果该元素有父元素,则margin-top会应用与父元素,子元素的top与父元素的top重叠.举例说明 <style>body{ma ...
- 基于DotNetOpenAuth的OAuth实现示例代码: 获取access token
1. 场景 根据OAuth 2.0规范,该场景发生于下面的流程图中的(D)(E)节点,根据已经得到的authorization code获取access token. 2. 实现环境 DotNetOp ...
- 前端学HTTP之连接管理
前面的话 HTTP连接是HTTP报文传输的关键通道.要掌握HTTP就需要理解HTTP连接的来龙去脉以及如何使用这些连接 如果想查看一个网页,浏览器收到URL时,会执行下图所示的步骤.将服务器的IP地址 ...
- Hawk 4.3 转换器
转换器是最为常用的一种类型,当然它的使用也是最复杂的. 转换器有三种子类型: A:单文档->单文档:例如仅将某一列的字符提取出来 B:单文档->多文档:典型的如从爬虫转换,每一行url都可 ...