設計模式:代理(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中可以隨意切換,同時能夠定義相同的必須的接口。這種透明性是非常有益的,在很多模式之中都有着體現。
三、總結
代理模式是一種常用的模式,只在必要的時候生成實例,也分為很多種類,主要是按照使用去分類的,比如本例的虛擬代理,以及其他的遠程代理,訪問控制代理等,我們主要要理解代理的實現本質,代理的意義,以及實際的用處。代理模式與裝飾器模式比較類似,都是持有了同類或父類的引用(委托機制),並且在函數之中調用了同類的方法來加工與同類同名的本類的相應方法,但是也有區別,代理模式是為了減輕被代理人的工作,在不得已的時候再去打擾被代理人,而裝飾器模式是為了產生新的功能,裝飾原有的屬性。