設計模式之Proxy(代理)


理解並使用設計模式,能夠培養我們良好的面向對象編程習慣,同時在實際應用中,可以如魚
得水,享受遊刃有餘的樂趣.
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 {
/**

* Permission to read object.
*/
public static final int READ = 0;
/**
* Permission to administer the entire sytem.
*/
public static final int SYSTEM_ADMIN = 1;
/**
* Permission to administer a particular forum.
*/
public static final int FORUM_ADMIN = 2;
/**
* Permission to administer a particular user.
*/
public static final int USER_ADMIN = 3;
/**
* Permission to administer a particular group.
*/
public static final int GROUP_ADMIN = 4;
/**
* Permission to moderate threads.
*/
public static final int MODERATE_THREADS = 5;
/**
* Permission to create a new thread.
*/
public static final int CREATE_THREAD = 6;
/**
* Permission to create a new message.
*/
public static final int CREATE_MESSAGE = 7;
/**
* Permission to moderate messages.
*/
public static final int MODERATE_MESSAGES = 8;

public boolean isSystemOrForumAdmin() {
return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]);
}
.....
}
因此,Forum 中各種操作權限是和ForumPermissions 定義的用戶級別有關係的,作爲接口
Forum 的實現:ForumProxy 正是將這種對應關係聯繫起來.比如,修改Forum 的名稱,只有論壇
管理者或系統管理者可以修改,代碼如下:
public class ForumProxy implements Forum {
private ForumPermissions permissions;
private Forum forum;
this.authorization = authorization;
public ForumProxy(Forum forum, Authorization authorization,
ForumPermissions permissions)
{
this.forum = forum;
this.authorization = authorization;
this.permissions = permissions;
}
.....
public void setName(String name) throws UnauthorizedException,
ForumAlreadyExistsException
{
//只有是系統或論壇管理者纔可以修改名稱
if (permissions.isSystemOrForumAdmin()) {
forum.setName(name);
}
else {
throw new UnauthorizedException();
}
}
...
設計模式(Patterns in Java) -- http://www.jdon.com
27
}
而DbForum 纔是接口Forum 的真正實現,以修改論壇名稱爲例:
public class DbForum implements Forum, Cacheable {
...
public void setName(String name) throws
ForumAlreadyExistsException {
....
this.name = name;
//這裏真正將新名稱保存到數據庫中
saveToDb();
....
}
...
}
凡是涉及到對論壇名稱修改這一事件,其他程序都首先得和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;
protected Authorization authorization;
protected ForumPermissions permissions;
public ForumFactoryProxy(Authorization authorization, ForumFactory factory,
ForumPermissions permissions)
{
this.factory = factory;
this.authorization = authorization;
this.permissions = permissions;
}
public Forum createForum(String name, String description)
throws UnauthorizedException, ForumAlreadyExistsException
{
//只有系統管理者纔可以創建forum
if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
Forum newForum = factory.createForum(name, description);
return new ForumProxy(newForum, authorization, permissions);
}
else {
throw new UnauthorizedException();
}
}
方法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 模式的應用.
更多Proxy 應用,見http://www.research.umbc.edu/~tarr/cs491/lectures/Proxy.pdf
Sun 公司的 Explore the Dynamic Proxy API Dynamic Proxy Classes
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章