设计模式:代理(Proxy)模式
设计模式:代理(Proxy)模式
一、前言
代理模式或许我们都听说过,至少知道代理(Proxy)这个东西的,否则看这篇博客也没任何意义的。什么叫做代理,代理是代替服务器去接受请求者的请求的中间人。我们也听说过代理服务器这个东西,它的作用就是帮助客户端去请求客户端想要的资源,为什么要通过这个代理呢,那是因为客户端直接去访问服务器会被拒绝(防火墙屏蔽),而代理服务器则可以直接访问服务器的,因此客户端通过代理服务器,将请求的内容交给代理服务器,代理服务器对这些内容进行重组换成自己的标识(IP),并且将请求的结果返回给用户,这样做的弊端就是访问的速度大打折扣,但是有总比没有强。那么我们的代理模式也是这个样子的,如果代理能够处理的东西(比如用户已经访问过的,服务器返回过来的没有过期的缓存),不用请求服务器了,直接返回给用户,而只有代理服务器不能处理的东西才会再次交给服务器(本人)去处理,当然这只是代理的一种策略就是为了能够加快访问速度,这样代理服务器就代替我们去访问服务器了。
代理代理,到底是代替谁去处理,大家可能有疑虑,因为用户有请求,代理服务器代替用户去请求和处理,这样说是代替用户的;可是代理有了自己的权限(缓存)之后看起来又像是代替服务器去处理一样,只有自己做不到的时候才会去麻烦这个被代理人(服务器),并且返回用户想要的数据,那么到底是代替谁去处理呢?!
让我们想一下日常生活,我们去找一个人办一件事,但是这个人可能身份很高,我们不能直接去见面,但是这样的人身边都是有代理(助理)的,因此我们找到了这个助理,告诉自己的问题,如果这个人说,这么简单的问题呀,还用麻烦我们的老板?!于是直接告诉了我答案和解决办法。那么我(客户)就美滋滋的走了,如果助理觉得问题很大,自己一个人不能解决,就会去向老板(服务器)请示,最终得到结果并且告诉我(客户端),这样来说代理一直是为老板(服务器)代理的,而客户有事情的话去找助理,助理不管自己处理还是向老板请示都是一种应该做的事情,不能说代理是为我们(客户)代理的,应该说代理是替老板代理的,因此代理代理,是替服务器代理,而不是替客户端代理,当然相信大家也听说过反向代理,这个东西就是自己组建了服务器集群,然后使用反向代理机制加快别人访问自己服务器的速度,这样自己的集群就是被代理(老板)的了。我想这应该是一个误区,大多数人可能都搞不清的地方。
二、代码
本人去处理所有事情是非常麻烦的,特别是初始化的时候都非常的耗时,因此使用代理,不到必须自己出马的时候一直按兵不动,让代理去完成这些工作,这就是代理模式。
Printable接口:代理的同源性:
1 package zyr.dp.proxy; 2 3 public interface Printable { 4 5 public abstract void setPrinterName(String name); 6 public abstract String getPrinterName(); 7 public abstract void print(String word); 8 9 }
Printer类:本人(相当于真正的服务器)
1 package zyr.dp.proxy; 2 3 public class Printer implements Printable { 4 5 String name; 6 public Printer(String name) { 7 this.name=name; 8 heavyWork(); 9 System.out.println("生成打印机实例成功..."); 10 } 11 12 //做一点重活 13 private void heavyWork() { 14 System.out.println("本人:"+name); 15 try { 16 Thread.sleep(5000); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 } 21 22 public void setPrinterName(String name) { 23 this.name=name; 24 } 25 public String getPrinterName() { 26 return name; 27 } 28 29 public void print(String word) { 30 System.out.println("打印机"+name+"正在打印..."); 31 System.out.println(word); 32 System.out.println("打印完成!"); 33 } 34 35 }
ProxyPrinter代理类:
1 package zyr.dp.proxy; 2 3 public class ProxyPrinter implements Printable { 4 5 String name; 6 Printer printer=null; 7 8 //代理能做的事情自己去做 9 public synchronized void setPrinterName(String name) { 10 if(printer!=null){ 11 printer.setPrinterName(name); 12 } 13 this.name=name; 14 } 15 16 //代理能做的事情自己去做 17 public String getPrinterName() { 18 return name; 19 } 20 21 //代理做不了的事情交给真正能做的(打印机)去做 22 public void print(String word) { 23 check(); 24 printer.print(word); 25 } 26 27 private synchronized void check() { 28 if(printer==null){ 29 printer=new Printer(name); 30 } 31 } 32 33 }
Main类:
1 package zyr.dp.proxy; 2 3 public class Main { 4 5 public static void main(String[] args) { 6 7 Printable proxy=new ProxyPrinter(); 8 proxy.setPrinterName("zyr"); 9 System.out.println("此时代理的名字为:"+proxy.getPrinterName()); 10 System.out.println("==遇到了代理处理不了的工作,通知服务器=="); 11 proxy.print("hello,world!"); 12 System.out.println("===================="); 13 proxy.setPrinterName("lsx"); 14 System.out.println("此时代理的名字为:"+proxy.getPrinterName()); 15 proxy.print("hello,my country!"); 16 } 17 18 }
运行结果:
可以看到服务器的启动实在是太耗时了(睡眠5秒钟来表示),那么使用代理服务器可以轻松地处理一些事务(设置名字,获得名字),直到代理服务器无能为力的时候(print打印内容),代理服务器就会通知服务器(本人)让服务器去处理,从本例可以看到,代理是清楚的直到被代理的对象的,因为使用了委托机制,将Printer对象组合进来),但是Printer是不知道代理的,它只是被启动了而已,这说明了什么?!本人(Printer)可以不做任何改动,就可以增加很多的代理去启动本人,这样非常利于可扩展性,其实这里也使用了懒加载模式,可以看到只有到不得不使用的时候才生成被代理的实例,那么可不可以直接在代理模式之中使用懒加载机制呢,答案是不利于可扩展性,没有这种分而治之的思想好,另外就是启动Printer类本身就是一种开销。同时我们看到了代理和被代理人都实现了同样的接口,这样的好处是很大的,在Main中可以随意切换,同时能够定义相同的必须的接口。这种透明性是非常有益的,在很多模式之中都有着体现。
三、总结
代理模式是一种常用的模式,只在必要的时候生成实例,也分为很多种类,主要是按照使用去分类的,比如本例的虚拟代理,以及其他的远程代理,访问控制代理等,我们主要要理解代理的实现本质,代理的意义,以及实际的用处。代理模式与装饰器模式比较类似,都是持有了同类或父类的引用(委托机制),并且在函数之中调用了同类的方法来加工与同类同名的本类的相应方法,但是也有区别,代理模式是为了减轻被代理人的工作,在不得已的时候再去打扰被代理人,而装饰器模式是为了产生新的功能,装饰原有的属性。