理解並使用設計模式,可以培養咱們良好的面向對象編程習慣,同時在實際應用中,能夠如魚得水,享受遊刃有餘的樂趣。
Proxy是比較有用途的一種模式,並且變種較多,應用場合覆蓋從小結構到整個系統的大結構,Proxy是代理的意思,咱們也許有代理服務器等概念,代理概念能夠解釋爲:在出發點到目的地之間有一道中間層,意爲代理。
設計模式中定義:爲其餘對象提供一種代理以控制對這個對象的訪問。html
舉例兩個具體狀況:數據庫
總之原則是,對於開銷很大的對象,只有在使用它時才建立,這個原則能夠爲咱們節省不少寶貴的Java內存。因此,有些人認爲Java耗費資源內存,我覺得這和程序編制思路也有必定的關係。編程
以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(); } } ... }
而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談入深一點,下面要涉及到工廠模式了。
咱們已經知道,使用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。併發
public void foFetches(Hashtable ht){ Hashttable newht=(Hashtable)ht.clone(); }
問題又來了,因爲是針對clone出來的對象操做,若是原來的母體被其餘客戶端操做修改了,那麼對clone出來的對象操做就沒有意義了。
最後解決方案:咱們能夠等其餘客戶端修改完成後再進行clone,也就是說,這個特別的客戶端先經過調用一個叫clone的方法來進行一系列數據獲取操做。但實際上沒有真正的進行對象拷貝,直至有其餘客戶端修改了這個對象Collection。
使用Proxy實現這個方案,這就是copy-on-write操做。
Proxy應用範圍很廣,如今流行的分佈計算方式RMI和Corba等都是Proxy模式的應用。編輯器
系列文章:
Java設計模式(5)共享模式/享元模式(Flyweight模式)
Java設計模式(14)責任鏈模式(Chain of Responsibility模式)