理解並使用設計模式 , 能夠培養我們良好的面向物件編程習慣 , 同時在實際應用中 , 可以如魚得水 , 享受遊刃有餘的樂趣。
Proxy 是比較有用途的一種模式 , 而且變種較多 , 應用場合覆蓋從小結構到整個系統的大結構 ,Proxy 是代理的意思 , 我們也許有代理伺服器等概念 , 代理概念可以解釋爲 : 在出發點到目的地之間有一道中間層 , 意爲代理。
設計模式中定義 : 爲其他物件提供一種代理以控制對這個物件的訪問。
爲什麽要使用 Proxy?
1. 授權機制 不同級別的用戶對同一物件擁有不同的訪問權利 , 如 Jive 論壇系統中 , 就使用 Proxy 進行授權機制控制 , 訪問論壇有兩種人 : 註冊用戶和遊客 ( 未註冊用戶 ),Jive 中就通過類似 ForumProxy 這樣的代理來控制這兩種用戶對論壇的訪問許可權。
2. 某個用戶端不能直接操作到某個物件 , 但又必須和那個物件有所互動。
舉例兩個具體情況 :
(1) 如果那個物件是一個是很大的圖片 , 需要花費很長時間才能顯示出來 , 那麽當這個圖片包含在文檔中時 , 使用編輯器或瀏覽器打開這個文檔 , 打開文檔必須很迅速 , 不能等待大圖片處理完成 , 這時需要做個圖片 Proxy 來代替真正的圖片。
(2) 如果那個物件在 Internet 的某個遠端伺服器上 , 直接操作這個物件因爲網路速度原因可能比較慢 , 那我們可以先用 Proxy 來代替那個物件。
總之原則是 , 對於開銷很大的物件 , 只有在使用它時才創建 , 這個原則可以爲我們節省很多寶貴的 Java 記憶體。 所以 , 有些人認爲 Java 耗費資源記憶體 , 我以爲這和程式編制思路也有一定的關係。
如何使用 Proxy?
以 Jive論壇系統 爲例 , 訪問論壇系統的用戶有多種類型 : 註冊普通用戶 論壇管理者 系統管理者 遊客 , 註冊普通用戶才能發言 ; 論壇管理者可以管理他被授權的論壇 ; 系統管理者可以管理所有事務等 , 這些許可權劃分和管理是使用 Proxy 完成的。
Forum 是 Jive 的核心介面 , 在 Forum 中陳列了有關論壇操作的主要行爲 , 如論壇名稱 論壇描述的獲取和修改 , 帖子發表刪除編輯等。
在 ForumPermissions 中定義了各種級別許可權的用戶 :
public class ForumPermissions implements Cacheable { /** /** /** /** /** /** /** /** /** ..... public boolean isSystemOrForumAdmin() { ..... } |
因此 ,Forum 中各種操作許可權是和 ForumPermissions 定義的用戶級別有關係的 , 作爲介面 Forum 的實現 :ForumProxy 正是將這種對應關係聯繫起來。比如 , 修改 Forum 的名稱 , 只有論壇管理者或系統管理者可以修改 , 代碼如下 :
public class ForumProxy implements Forum { private ForumPermissions permissions; public void setName(String name) throws UnauthorizedException, ... } |
而 DbForum 才是介面 Forum 的真正實現 , 以修改論壇名稱爲例 :
public class DbForum implements Forum, Cacheable { ... public void setName(String name) throws ForumAlreadyExistsException { ....
} |
凡是涉及到對論壇名稱修改這一事件 , 其他程式都首先得和 ForumProxy 打交道 , 由 ForumProxy 決定是否有許可權做某一樣事情 ,ForumProxy 是個名副其實的 " 閘道 "," 安全代理系統 " 。
在平時應用中 , 無可避免總要涉及到系統的授權或安全體系 , 不管你有無意識的使用 Proxy, 實際你已經在使用 Proxy 了。
我們繼續結合 Jive 談入深一點 , 下面要涉及到工廠模式了 , 如果你不瞭解工廠模式 , 請看我的另外一篇文章 :設計模式之 Factory
我們已經知道 , 使用 Forum 需要通過 ForumProxy,Jive 中創建一個 Forum 是使用 Factory 模式 , 有一個總的抽象類別 ForumFactory, 在這個抽象類別中 , 呼叫 ForumFactory 是通過 getInstance() 方法實現 , 這裏使用了 Singleton( 也是設計模式之一 , 由於介紹文章很多 , 我就不寫了 ,看這裏 ),getInstance() 返回的是 ForumFactoryProxy 。
爲什麽不返回 ForumFactory, 而返回 ForumFactory 的實現 ForumFactoryProxy?
原因是明顯的 , 需要通過代理確定是否有許可權創建 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 和創建 Forum;
至於爲什麽將使用物件和創建物件分開 , 這也是爲什麽使用 Factory 模式的原因所在 : 是爲了 " 封裝 " " 分派 "; 換句話說 , 盡可能功能單一化 , 方便維護修改。
Jive 論壇系統中其他如帖子的創建和使用 , 都是按照 Forum 這個思路而來的。
以上我們討論了如何使用 Proxy 進行授權機制的訪問 ,Proxy 還可以對用戶隱藏另外一種稱爲 copy-on-write 的優化方式。拷貝一個龐大而複雜的物件是一個開銷很大的操作 , 如果拷貝過程中 , 沒有對原來的物件有所修改 , 那麽這樣的拷貝開銷就沒有必要。用代理延遲這一拷貝過程。
比如 : 我們有一個很大的 Collection, 具體如 hashtable, 有很多用戶端會並發同時訪問它。其中一個特別的用戶端要進行連續的資料獲取 , 此時要求其他用戶端不能再向 hashtable 中增加或刪除 東東。
最直接的解決方案是 : 使用 collection 的 lock, 讓這特別的用戶端獲得這個 lock, 進行連續的資料獲取 , 然後再釋放 lock 。
public void foFetches(Hashtable ht){
synchronized(ht){
// 具體的連續資料獲取動作。。
}
}
但是這一辦法可能鎖住 Collection 會很長時間 , 這段時間 , 其他用戶端就不能訪問該 Collection 了。
第二個解決方案是 clone 這個 Collection, 然後讓連續的資料獲取針對 clone 出來的那個 Collection 操作。這個方案前提是 , 這個 Collection 是可 clone 的 , 而且必須有提供深度 clone 的方法。 Hashtable 就提供了對自己的 clone 方法 , 但不是 Key 和 value 物件的 clone, 關於 Clone 含義可以參考 專門文章 。
public void foFetches(Hashtable ht){
Hashttable newht=(Hashtable)ht.clone();
}
問題又來了 , 由於是針對 clone 出來的物件操作 , 如果原來的母體被其他用戶端操作修改了 , 那麽對 clone 出來的物件操作就沒有意義了。
最後解決方案 : 我們可以等其他用戶端修改完成後再進行 clone, 也就是說 , 這個特別的用戶端先通過呼叫一個叫 clone 的方法來進行一系列資料獲取操作。但實際上沒有真正的進行物件拷貝 , 直至有其他用戶端修改了這個物件 Collection 。
使用 Proxy 實現這個方案。這就是 copy-on-write 操作。
Proxy 應用範圍很廣 , 現在流行的分佈計算方式 RMI 和 Corba 等都是 Proxy 模式的應用。