一個故事
話說,張三最近看上了隔壁班的班花小鳳,但是張三很靦腆,不好意思直接跟小鳳說,臨近小鳳生日了,張三想送禮物(鮮花,巧克力)給小鳳,於是張三的朋友們開始給他支招。
李四的想法
李四比較乾脆直接,方案就是:啥也不說,直接上啊。於是李四把這段關係抽象成了追求者(張三)和被追求者(小鳳)
被追求者(小鳳):
//被追求者
public class Girl{ String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
追求者(張三)
public class Pursuit{ Girl schoolGirl; String name; public Pursuit(String name,Girl gils){
this.name = name;
this.schoolGirl = gils; } public void giveFlows(){ System.out.println(gils.getName()+","+"這是送你的鮮花"); } public void giveChocolate(){ System.out.println(gils.getName()+","+"這是送你的巧克力"); } }
實際效果:
public class Test{ public static viod main(String args[]){ Girl xiaofeng = new Girl(); xiaofeng.setName("小鳳"); Pursuit zhangsan = new Pursuit("張三",xiaofeng); zhangsan.giveFlows(); zhangsan.giveChocolate(); } }
張三看完了,摸了摸腦袋:“李四你真是虎批一個……,我就這樣送,萬一被拒絕了,我還要不要面子了?!”
王五的想法
王五說既然老三你這麼靦腆,我來給你支個招,你把禮物給我,我替你去送。
Public Class Middleman{ String name; Purisuit p public Middleman(String name,Girl girl){ Purisuit p = new Purisuit(name,girl); } public void giveFlows(){ p.giveFlows(); } public void giveCholocate(){ p.giveCholocate(); } }
public class Test{ public static viod main(String args[]){ Girl xiaofeng = new Girl(); xiaofeng.setName("小鳳"); Middleman wangwu = new Middleman("王五",xiaofeng); wangwu.giveFlows(); wangwu.giveChocolate(); } }
張三看完,沒差點氣過去,“好傢伙,我花錢買花,買巧克力,你**的白嫖啊,真當自己是老王了。”
王五:“你不是不好意思麼,我臉皮厚,嘿嘿”
張三的思考
老王的思路雖然沒皮沒臉,但多多少少給了我一點靈感,既然不好意思自己去,我幹嘛不找個中間人呢,讓中間人代我去,這樣小鳳就直接通過中間人拿到我的禮物了,還不用我當面去,即便被拒絕了也不丟人,嘿嘿……,但是我花了錢,不能沒我的事,我不能白當冤大頭……
下面我們進行一下分析,類似於送東西這種事,它屬於跑腿(方法),誰都可以,應該把它剝離出來,然後我作為追求者,老王作為中間人都有這個能力,追求者和中間者可以抽象成兩個角色的類別,想著想著,張三有了思路:
什麼是代理模式
經過上面張三的故事,我們聯想到現實生活中,超市裡買酒水,不就是這種方式麼,就是由廠家提供酒,然後找代理商來售賣。這種思路其實在物件導向裡有更為形象的描述,那就是代理模式。
定義與特點
代理模式的定義:由於某些原因需要給某物件提供一個代理以控制對該物件的訪問。這時,訪問物件不適合或者不能直接引用目標物件,代理物件作為訪問物件和目標物件之間的中介。
代理模式的主要優點有:
-
-
- 代理模式在客戶端與目標物件之間起到一箇中介作用和保護目標物件的作用;
- 代理物件可以擴充套件目標物件的功能;
- 代理模式能將客戶端與目標物件分離,在一定程度上降低了系統的耦合度,增加了程式的可擴充套件性
-
其主要缺點是:
-
-
- 代理模式會造成系統設計中類的數量增加
- 在客戶端和目標物件之間增加一個代理物件,會造成請求處理速度變慢;
- 增加了系統的複雜度;
-
結構與實現
代理模式的結構比較簡單,主要是通過定義一個繼承抽象主題的代理來包含真實主題,從而實現對真實主題的訪問,下面來分析其基本結構和實現方法。
結構組成:
代理模式的主要角色如下。
- 抽象主題(Subject)類:通過介面或抽象類宣告真實主題和代理物件實現的業務方法。
- 真實主題(Real Subject)類:實現了抽象主題中的具體業務,是代理物件所代表的真實物件,是最終要引用的物件。
- 代理(Proxy)類:提供了與真實主題相同的介面,其內部含有對真實主題的引用,它可以訪問、控制或擴充套件真實主題的功能。
其實現UML如圖:
在程式碼中,一般代理會被理解為程式碼增強,實際上就是在原始碼邏輯前後增加一些程式碼邏輯,而使呼叫者無感知。
根據代理的建立時期,代理模式分為靜態代理和動態代理。
-
- 靜態:由程式設計師建立代理類或特定工具自動生成原始碼再對其編譯,在程式執行前代理類的 .class 檔案就已經存在了。
- 動態:在程式執行時,運用反射機制動態建立而成
知道了以上的知識,我們對一開始的需求進行分析,並優化需求的實現
public class ProxyTest { public static void main(String[] args) { Proxy proxy = new Proxy(); proxy.Request(); } } //抽象主題 interface Subject { void Request(); } //真實主題 class RealSubject implements Subject { public void Request() { System.out.println("訪問真實主題方法..."); } } //代理 class Proxy implements Subject { private RealSubject realSubject; public void Request() { if (realSubject == null) { realSubject = new RealSubject(); } preRequest(); realSubject.Request(); postRequest(); } public void preRequest() { System.out.println("訪問真實主題之前的預處理。"); } public void postRequest() { System.out.println("訪問真實主題之後的後續處理。"); } }
這裡代理模式和UML模型圖會不會感覺很熟悉,如果不是很熟悉,可以去看看裝飾者模式,這兩個模式之間很像,具體的區別這裡不做過多的區別了,感興趣可以看一下:代理模式和裝飾者模式的區別
代理模式的應用場景
當無法或不想直接引用某個物件或訪問某個物件存在困難時,可以通過代理物件來間接訪問。使用代理模式主要有兩個目的:一是保護目標物件,二是增強目標物件。
前面分析了代理模式的結構與特點,現在來分析以下的應用場景。
- 遠端代理,這種方式通常是為了隱藏目標物件存在於不同地址空間的事實,方便客戶端訪問。例如,使用者申請某些網盤空間時,會在使用者的檔案系統中建立一個虛擬的硬碟,使用者訪問虛擬硬碟時實際訪問的是網盤空間。
- 虛擬代理,這種方式通常用於要建立的目標物件開銷很大時。例如,下載一幅很大的影像需要很長時間,因某種計算比較複雜而短時間無法完成,這時可以先用小比例的虛擬代理替換真實的物件,消除使用者對伺服器慢的感覺。
- 安全代理,這種方式通常用於控制不同種類客戶對真實物件的訪問許可權。
- 智慧指引,主要用於呼叫目標物件時,代理附加一些額外的處理功能。例如,增加計算真實物件的引用次數的功能,這樣當該物件沒有被引用時,就可以自動釋放它。
- 延遲載入,指為了提高系統的效能,延遲對目標的載入。例如,Hibernate中就存在屬性的延遲載入和關聯表的延時載入。
代理模式的擴充套件(動態代理)
在前面介紹的代理模式中,代理類中包含了對真實主題的引用,這種方式存在兩個缺點。
- 真實主題與代理主題一一對應,增加真實主題也要增加代理。
- 設計代理以前真實主題必須事先存在,不太靈活。採用動態代理模式可以解決以上問題,如 Spring的AOP,其結構圖如圖所示。
該型別文章目錄:設計模式彙總