設計模式之Proxy(代理)
板橋里人banq http://www.jdon.com 2002/04/21/
理解并使用設計模式,能夠培養(yǎng)我們良好的面向?qū)ο缶幊塘晳T,同時在實際應用中,可以如魚得水,享受游刃有余的樂趣.
代理模式是比較有用途的一種模式,而且變種較多,應用場合覆蓋從小結構到整個系統(tǒng)的大結構,Proxy是代理的意思,我們也許有代理服務器等概念,代理概念可以解釋為:在出發(fā)點到目的地之間有一道中間層,意為代理.
設計模式中定義: 為其他對象提供一種代理以控制對這個對象的訪問.
為什么要使用Proxy?
1.授權機制 不同級別的用戶對同一對象擁有不同的訪問權利,如Jive論壇系統(tǒng)中,就使用Proxy進行授權機制控制,訪問論壇有兩種人:注冊用戶和游客(未注冊用戶),Jive中就通過類似ForumProxy這樣的代理來控制這兩種用戶對論壇的訪問權限.
2.某個客戶端不能直接操作到某個對象,但又必須和那個對象有所互動.
舉例兩個具體情況:
(1)如果那個對象是一個是很大的圖片,需要花費很長時間才能顯示出來,那么當這個圖片包含在文檔中時,使用編輯器或瀏覽器打開這個文檔,打開文檔必須很迅速,不能等待大圖片處理完成,這時需要做個圖片Proxy來代替真正的圖片.
(2)如果那個對象在Internet的某個遠端服務器上,直接操作這個對象因為網(wǎng)絡速度原因可能比較慢,那我們可以先用Proxy來代替那個對象.
總之原則是,對于開銷很大的對象,只有在使用它時才創(chuàng)建,這個原則可以為我們節(jié)省很多寶貴的Java內(nèi)存. 所以,有些人認為Java耗費資源內(nèi)存,我以為這和程序編制思路也有一定的關系.
如何使用Proxy?
以Jive論壇系統(tǒng)為例,訪問論壇系統(tǒng)的用戶有多種類型:注冊普通用戶 論壇管理者 系統(tǒng)管理者 游客,注冊普通用戶才能發(fā)言;論壇管理者可以管理他被授權的論壇;系統(tǒng)管理者可以管理所有事務等,這些權限劃分和管理是使用Proxy完成的.
Forum是Jive的核心接口,在Forum中陳列了有關論壇操作的主要行為,如論壇名稱 論壇描述的獲取和修改,帖子發(fā)表刪除編輯等.
在ForumPermissions中定義了各種級別權限的用戶:
public class ForumPermissions implements Cacheable {
/** /** /** /** /** /** /** /** /** ..... public boolean isSystemOrForumAdmin() { ..... } |
因此,Forum中各種操作權限是和ForumPermissions定義的用戶級別有關系的,作為接口Forum的實現(xiàn):ForumProxy正是將這種對應關系聯(lián)系起來.比如,修改Forum的名稱,只有論壇管理者或系統(tǒng)管理者可以修改,代碼如下:
public class ForumProxy implements Forum { private ForumPermissions permissions; public void setName(String name) throws UnauthorizedException, ... } |
而DbForum才是接口Forum的真正實現(xiàn),以修改論壇名稱為例:
public class DbForum implements Forum, Cacheable { public void setName(String name) throws ForumAlreadyExistsException { ....
} |
凡是涉及到對論壇名稱修改這一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy決定是否有權限做某一樣事情,ForumProxy是個名副其實的"網(wǎng)關","安全代理系統(tǒng)".
在平時應用中,無可避免總要涉及到系統(tǒng)的授權或安全體系,不管你有無意識的使用Proxy,實際你已經(jīng)在使用Proxy了.
我們繼續(xù)結合Jive談入深一點,下面要涉及到工廠模式了,如果你不了解工廠模式,請看我的另外一篇文章:設計模式之Factory
我們已經(jīng)知道,使用Forum需要通過ForumProxy,Jive中創(chuàng)建一個Forum是使用Factory模式,有一個總的抽象類ForumFactory,在這個抽象類中,調(diào)用ForumFactory是通過getInstance()方法實現(xiàn),這里使用了Singleton(也是設計模式之一,由于介紹文章很多,我就不寫了,看這里),getInstance()返回的是ForumFactoryProxy.
為什么不返回ForumFactory,而返回ForumFactory的實現(xiàn)ForumFactoryProxy?
原因是明顯的,需要通過代理確定是否有權限創(chuàng)建forum.
在ForumFactoryProxy中我們看到代碼如下:
public class ForumFactoryProxy extends ForumFactory {
protected ForumFactory factory; public ForumFactoryProxy(Authorization authorization, ForumFactory factory, public Forum createForum(String name, String description) |
方法createForum返回的也是ForumProxy, Proxy就象一道墻,其他程序只能和Proxy交互操作.
注意到這里有兩個Proxy:ForumProxy和ForumFactoryProxy. 代表兩個不同的職責:使用Forum和創(chuàng)建Forum;
至于為什么將使用對象和創(chuàng)建對象分開,這也是為什么使用Factory模式的原因所在:是為了"封裝" "分派";換句話說,盡可能功能單一化,方便維護修改.
Jive論壇系統(tǒng)中其他如帖子的創(chuàng)建和使用,都是按照Forum這個思路而來的.
以上我們討論了如何使用Proxy進行授權機制的訪問,Proxy還可以對用戶隱藏另外一種稱為copy-on-write的優(yōu)化方式.拷貝一個龐大而復雜的對象是一個開銷很大的操作,如果拷貝過程中,沒有對原來的對象有所修改,那么這樣的拷貝開銷就沒有必要.用代理延遲這一拷貝過程.
比如:我們有一個很大的Collection,具體如hashtable,有很多客戶端會并發(fā)同時訪問它.其中一個特別的客戶端要進行連續(xù)的數(shù)據(jù)獲取,此時要求其他客戶端不能再向hashtable中增加或刪除 東東.
最直接的解決方案是:使用collection的lock,讓這特別的客戶端獲得這個lock,進行連續(xù)的數(shù)據(jù)獲取,然后再釋放lock.
public void foFetches(Hashtable ht){
synchronized(ht){
//具體的連續(xù)數(shù)據(jù)獲取動作..
}
}
但是這一辦法可能鎖住Collection會很長時間,這段時間,其他客戶端就不能訪問該Collection了.
第二個解決方案是clone這個Collection,然后讓連續(xù)的數(shù)據(jù)獲取針對clone出來的那個Collection操作.這個方案前提是,這個Collection是可clone的,而且必須有提供深度clone的方法.Hashtable就提供了對自己的clone方法,但不是Key和value對象的clone,關于Clone含義可以參考專門文章.
public void foFetches(Hashtable ht){
Hashttable newht=(Hashtable)ht.clone();
}
問題又來了,由于是針對clone出來的對象操作,如果原來的母體被其他客戶端操作修改了, 那么對clone出來的對象操作就沒有意義了.
最后解決方案:我們可以等其他客戶端修改完成后再進行clone,也就是說,這個特別的客戶端先通過調(diào)用一個叫clone的方法來進行一系列數(shù)據(jù)獲取操作.但實際上沒有真正的進行對象拷貝,直至有其他客戶端修改了這個對象Collection.
使用Proxy實現(xiàn)這個方案.這就是copy-on-write操作.
Proxy應用范圍很廣,現(xiàn)在流行的分布計算方式RMI和Corba等都是Proxy模式的應用.
更多Proxy應用,見http://www.research.umbc.edu/~tarr/cs491/lectures/Proxy.pdf
Sun公司的 Explore the Dynamic Proxy APIDynamic Proxy Classes
?