一个江南皮鞋厂的小故事带我了解透了——什么是“代理商模式”
引子
小帅前几年开了一家鞋厂,专门生产男鞋,做批发生意,刚开始生意还不错,日子过得蛮滋润的。
不料,好景不长,眼看这两年订单量直线下降,生产线上的工人看赚不到钱,于是纷纷离任,只剩下几个老工友,偶尔有个略微大一点的单子竟也接不动了。
这样下去就只能关门大吉了,小帅不得不另寻出路。前几日业内好朋友聚会,好友刘望星的工厂生意甚是红火,让小帅羡慕不已。
小帅心想,为何不把自己的订单直接给刘望星代工生产呢?把自己的工厂干脆关了,就负责接单,做个代理商商得了!
小帅和刘望星一拍即合,合作正式开始了。
小帅的任务无非就是接单,发货,为了不让用户知道自己只是个代理商商,小帅特意让工厂先把货发到自己这里,贴上自己的牌子,而后自己再把货发给用户,让用户以为鞋子都是自己的工厂生产的。
代理商模式
整个过程用代码展现如下:
/** * 鞋厂接口 */public interface IShoesFactory { /** * 参观工厂 */ void visitFactory(); /** * 下订单 * @param price 金额 */ void placeOrder(double price); /** * 发货 */ void ship();}
/** * 鞋厂类 */public class ShoesFactory implements IShoesFactory{ private String name; public ShoesFactory(String name) { this.name = name; } /** * 参观工厂 */ @Override public void visitFactory() { System.out.println("带用户参观" + name); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { System.out.println("接到" + price + "元的订单"); } /** * 发货 */ @Override public void ship() { System.out.println("开始发货"); }}
/** * 代理商鞋厂类 */public class ShoesFactoryProxy implements IShoesFactory{ private IShoesFactory shoesFactory; public ShoesFactoryProxy(IShoesFactory shoesFactory) { this.shoesFactory = shoesFactory; } /** * 参观工厂 */ @Override public void visitFactory() { shoesFactory.visitFactory(); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { shoesFactory.placeOrder(price); } /** * 发货 */ @Override public void ship() { shoesFactory.ship(); }}
用户类:
public class Client { public static void main(String[] args) { IShoesFactory factory = new ShoesFactory("刘望星的工厂"); IShoesFactory factoryProxy = new ShoesFactoryProxy(factory); // 参观工厂 factoryProxy.visitFactory(); // 下订单 factoryProxy.placeOrder(10000); // 发货 factoryProxy.ship(); }}
输出结果:
带用户参观刘望星的工厂接到10000.0元的订单开始发货
用户要参观工厂,小帅就带用户去参观刘望星的工厂,和用户说这就是自己的工厂。用户向小帅下单,小帅就把订单下给刘望星生产。
用户类调用代理商的方法,代理商类再调用业务类的方法完成工作。
代理商模式定义
代理商模式(Proxy Pattern) :给某一个对象提供一个代理商,并由代理商对象控制对原对象的引用。 代理商模式的英语叫Proxy,它是一种<typo id=”typo-2356″ data-origin=”结构型” ignoretag=”true”>结构型</typo>模式。
代理商背后一般至少有一个实际对象,代理商的外部功能和实际对象一般是一样的,客户与代理商打交道,不直接接触实际对象,甚至不知道实际对象的存在。
代理商模式究竟有什么用呢?难道仅仅是通过代理商类做个转发吗?
当然不是的,尽管外部功能和实际对象一样,但代理商有它存在的价值,代理商也分为多种类型比方:
- 远程(Remote)代理商:适用于调用远程服务器对象的情况,代理商通过网络传递用户端请求, 负责解决所有与网络相关的复杂细节,简化了用户端的调用。
- 虚拟(Virtual)代理商:假如需要创立一个资源消耗较大的对象,先创立一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创立。
- Copy-on-Write代理商:它是虚拟代理商的一种,把复制(克隆)操作推迟到只有在用户端真正需要时才执行。一般来说,对象的深克隆是一个 开销较大的操作,Copy-on-Write代理商可以让这个操作推迟,只有对象被用到的时候才被克隆。
- 保护(Protect or Access)代理商:控制对一个对象的访问,可以给不同的客户提供不同级别的使用权限。
- 缓冲(Cache)代理商:为某一个目标操作的结果提供临时的存储空间,以便多个用户端可以共享这些结果。
- 防火墙(Firewall)代理商:保护目标不让恶意客户接近。
- 同步化(Synchronization)代理商:使几个客户能够同时使用一个对象而没有冲突。
- 智能引用(Smart Reference)代理商:当一个对象被引用时,提供少量额外的操作,如将此对象被调用的次数记录下来等。
我们来看看几个例子:
保护代理商
保护(Protect or Access)代理商的作用是,控制对一个对象的访问,可以给不同的客户提供不同级别的使用权限。
假如我们不<typo id=”typo-3194″ data-origin=”想” ignoretag=”true”>想</typo>用户访问某个功能,即可以在代理商类里做手脚,毕竟业务类都是通过代理商类调用的嘛。
比方,小帅怕露馅,不想让用户参观工厂,即可以改写参观工厂的方法:
输出:
工厂禁止参观接到10000.0元的订单开始发货
智能引用代理商
智能引用(Smart Reference)代理商的作用是,当一个对象被引用时,提供少量额外的操作,如将此对象被调用的次数记录下来等。
比方,小帅作为代理商商,一定是要赚钱的,用户下了一个10000元的订单,小帅就和刘望星谈好提成10%,即可以在代理商类中把金额扣掉。
还有,小帅的工厂有自己的品牌,发货之前还要贴上自己的牌子。
输出:
工厂禁止参观接到9000.0元的订单贴上小帅牌标签。开始发货
代理商模式的几种应用
图片代理商:一个很常见的代理商模式的应用实例就是对大图浏览的控制。客户通过浏览器访问网页时先不加载真实的大图,而是通过代理商对象的方法来进行解决。
在代理商对象的方法中,先使用一个线程向用户端浏览器加载一个小图片,而后在后端使用另一个线程来调用大图片的加载方法将大图片加载到用户端。当需要浏览大图片时,再将大图片在新网页中显示。假如客户在浏览大图时加载工作还没有完成,可以再启动一个线程来显示相应的提醒信息。通过代理商技术结合多线程编程将真实图片的加载放到后端来操作,不影响前端图片的浏览。
远程代理商:远程代理商可以将网络的细节隐藏起来,使得用户端不必考虑网络的存在。用户完全可以认为被代理商的远程业务对象是局域的而不是远程的,而远程代理商对象承担了大部分的网络通信工作。
虚拟代理商:当一个对象的加载十分耗费资源的时候,虚拟代理商的优势就非常显著地表现出来了。虚拟代理商模式是一种内存节省技术,那些占用大量内存或者解决复杂的对象将延迟到使用它的时候才创立。
动态代理商
上面说的这些方法是静态代理商模式,真实的业务类必需是事前已经创立好的的,并把它传给代理商对象,作为一个内部成员。
静态代理商是这样子的:
假如一个真实业务类必需对应一个代理商类,这将导致系统中的代理商类的个数急剧添加,比方有10个不同的业务类,那么必需要有10个对应的代理商类,因而需要想办法减少系统中类的个数。
动态代理商可以在事前不知道真实业务类的情况下使用代理商类,在程序运行期间由JVM根据反射等机制动态的生成,动态代理商的典型应用就是Spring AOP。
下面我们看一下Java SDK动态代理商的例子:
/** * 鞋厂接口 */public interface IShoesFactory { /** * 参观工厂 */ void visitFactory(); /** * 下订单 * @param price 金额 */ void placeOrder(double price); /** * 发货 */ void ship();}
/** * 鞋厂类 */public class ShoesFactory implements IShoesFactory { private String name; public ShoesFactory(String name) { this.name = name; } /** * 参观工厂 */ @Override public void visitFactory() { System.out.println("带用户参观" + name); } /** * 下订单 * * @param price 金额 */ @Override public void placeOrder(double price) { System.out.println("接到" + price + "元的订单"); } /** * 发货 */ @Override public void ship() { System.out.println("开始发货"); }}
InvocationHandler接口是proxy代理商实例的调用解决程序实现的一个接口,代理商类每调用一次方法就会进入这里的invoke方法。
public class ShoesFactoryHandler implements InvocationHandler { /** * 被代理商的对象,实际的方法执行者 */ private Object proxiedObject; public ShoesFactoryHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; } /** * 代理商类每调用一次方法就会进入这里 * @param proxy 表示代理商对象本身,需要注意,它不是被代理商的对象 * @param method 表示正在被调用的方法 * @param args 表示方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /*if(method.getName().equals("visitFactory")) { System.out.println("工厂禁止参观"); }*/ if(method.getName().equals("placeOrder")) { // 假如是下订单方法,给代工厂9折的价格 method.invoke(proxiedObject, Double.parseDouble(String.valueOf(args[0])) * 0.9); } else if(method.getName().equals("ship")) { // 假如是发货方法,发货之前先贴上标签 System.out.println("贴上小帅牌标签"); method.invoke(proxiedObject, args); } else { // 其余方法,直接调用 method.invoke(proxiedObject, args); } return null; }}
Proxy类调用newProxyInstance方法来创立一个代理商对象,上面的InvocationHandler对象作为参数之一。
public class ShoesFactoryProxy { /** * 生成动态代理商 * @param proxiedObject 被代理商的对象,实际的方法执行者 * @return */ public Object createProxy(Object proxiedObject) { // 获取对应的ClassLoader ClassLoader classLoader = proxiedObject.getClass().getClassLoader(); // 获取所有的接口 Class[] interfaces = proxiedObject.getClass().getInterfaces(); // 创立一个传给代理商类的调用请求解决器 ShoesFactoryHandler handler = new ShoesFactoryHandler(proxiedObject); return Proxy.newProxyInstance(classLoader, interfaces, handler); }}
用户端类:
public class Client { public static void main(String[] args) { IShoesFactory factory = new ShoesFactory("刘望星的工厂"); ShoesFactoryProxy shoesFactoryProxy = new ShoesFactoryProxy(); IShoesFactory factoryProxy = (IShoesFactory)shoesFactoryProxy.createProxy(factory); // 参观工厂 factoryProxy.visitFactory(); // 下订单 factoryProxy.placeOrder(10000); // 发货 factoryProxy.ship(); }}
输出结果:
带用户参观刘望星的工厂接到9000.0元的订单贴上小帅牌标签开始发货
通用性更强些的动态代理商类一般是这样的:
public class NormalHandler implements InvocationHandler { /** * 被代理商的对象,实际的方法执行者 */ private Object proxiedObject; public NormalHandler(Object proxiedObject) { this.proxiedObject = proxiedObject; } /** * 代理商类每调用一次方法就会进入这里 * @param proxy 表示代理商对象本身,需要注意,它不是被代理商的对象 * @param method 表示正在被调用的方法 * @param args 表示方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 调用实际方法之前的操作 preRequest(); // 调用实际方法 Object result = method.invoke(proxiedObject, args); // 调用实际方法之后的操作 afterRequest(); } return result; }}
这样,我们就能在preRequest()和afterRequest()方法中做很多事情了,比方记录日志,记录调用时间等等。
总结
代理商模式看上去和装饰者模式很像,不过它们的用意是不一样的,装饰者模式是为对象加上新的行为,而代理商模式是作为真实对象的替身,控制对象的访问。
比方,保护代理商,进行权限控制,不让用户访问某些功能;比方,虚拟代理商,在真实的对象创立之前,先返回预约设定好的信息。
下面我们来看看代理商模式的优点和缺点:
优点
- 代理商模式能够协调调用者和被调用者,在肯定程度上降低了系统的耦合度。
- 你可以在用户端毫无察觉的情况下控制服务对象。
- 即便服务对象还未准备好或者不存在, 代理商也可以正常工作。
- 符合开闭准则, 你可以在不对服务或者用户端做出修改的情况下创立新代理商。
缺点
- 因为在用户端和真实业务类之间添加了代理商对象,因而有些类型的代理商模式可能会造成请求的解决速度变慢。
- 实现代理商模式需要额外的工作,添加了程序的复杂度。
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 一个江南皮鞋厂的小故事带我了解透了——什么是“代理商模式”