设计模式-代理模式(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就是不同的两条逻辑路径) ...
随机推荐
- C语言可以开发哪些项目?
C语言是我们大多数人的编程入门语言,对其也再熟悉不过了,不过很多初学者在学习的过程中难免会出现迷茫,比如:不知道C语言可以开发哪些项目,可以应用在哪些实际的开发中--,这些迷茫也导致了我们在学习的过程 ...
- 笔记:Memory Notification: Library Cache Object loaded into SGA
笔记:Memory Notification: Library Cache Object loaded into SGA在警告日志中发现一些这样的警告信息:Mon Nov 21 14:24:22 20 ...
- [jquery]显示隐藏div标签的几种方法
1.$("#demo").attr("style","display:none;");//隐藏div $("#demo" ...
- BZOJ 1010: [HNOI2008]玩具装箱toy [DP 斜率优化]
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 9812 Solved: 3978[Submit][St ...
- RavenDB官网文档翻译系列第一
本系列文章主要翻译自RavenDB官方文档,有些地方做了删减,有些内容整合在一起.欢迎有需要的朋友阅读.毕竟还是中文读起来更亲切吗.下面进入正题. 起航 获取RavenDB RavenDB可以通过Nu ...
- JQuery插件定义
一:导言 有些WEB开发者,会引用一个JQuery类库,然后在网页上写一写$("#"),$("."),写了几年就对别人说非常熟悉JQuery.我曾经也是这样的人 ...
- 写自己的Socket框架(一)
本系列仅介绍可用于生产环境的C#异步Socket框架,如果您在其他地方看到类似的代码,不要惊讶,那可能就是我在参考开源代码时,直接“剽窃”过来的. 1.在脑海里思考一下整个socket的链接的处理流程 ...
- 转 10 个最佳的 Node.js 的 MVC 框架
10 个最佳的 Node.js 的 MVC 框架 oschina 发布于: 2014年02月24日 (33评) 分享到: 收藏 +322 Node.js 是一个基于Chrome JavaScri ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- PagerSlidingTabStrip介绍及使用,让ViewPager更绚丽
转载请注明出处http://blog.csdn.net/harryweasley/article/details/42290595,谢谢. 以前一直想着,ViewPager中间的那个横线怎么跟着屏幕的 ...