1. 程式人生 > >設計模式:學習筆記(12)——代理模式

設計模式:學習筆記(12)——代理模式

設計模式:學習筆記(12)——代理模式

代理模式

  挾天子以令諸侯,曹操與漢獻帝的關係,映射了代理模式的。所謂代理者是指一個類別可以作為其他東西的介面。代理者使用代理物件完成使用者請求,遮蔽使用者對真實物件的訪問。在軟體設計中,使用代理模式的意圖有很多,比如因為安全原因需要遮蔽客戶端直接訪問真實物件,或者在遠端呼叫中需要使用代理類處理遠端方法呼叫的技術細節(如RMI),也可能為了提升系統性能,對真實物件進行封裝,從而達到延遲載入的目的。

 

延遲載入

  以一個簡單的示例來闡述使用代理模式實現延遲載入的方法及其意義。假設某客戶端軟體有根據使用者請求去資料庫查詢資料的功能。在查詢資料前,需要獲得資料庫連線,軟體開啟時初始化系統的所有類,此時嘗試獲得資料庫連線。當系統有大量的類似操作存在時 (比如 XML 解析等),所有這些初始化操作的疊加會使得系統的啟動速度變得非常緩慢。為此,使用代理模式的代理類封裝對資料庫查詢中的初始化操作,當系統啟動時,初始化這個代理類,而非真實的資料庫查詢類,而代理類什麼都沒有做。因此,它的構造是相當迅速的。

  在系統啟動時,將消耗資源最多的方法都是用代理模式分離,可以加快系統的啟動速度,減少使用者的等待時間。而在使用者真正做查詢時再由代理類取載入真實的資料庫查詢類,完成使用者請求。這個過程就是使用代理模式實現了延遲載入

  延遲載入的核心思想:如果當前並沒有使用這個元件,則不需要真正地初始化它,使用一個代理物件替代它原有的位置,只要在真正需要的時候才對他進行載入。

  下面是一個延遲載入代理示例:

public interface IDBQuery {
    String request();
}

public class DBQuery implements IDBQuery{
    public DBQuery(){
        try{
            Thread.sleep(1000);//假設資料庫連線等耗時操作
        }catch(InterruptedException ex){
            ex.printStackTrace();
        }
    }
 
    @Override
    public String request() {
    // TODO Auto-generated method stub
    return "request string";
    }
  
}

public class DBQueryProxy implements IDBQuery{
    private DBQuery real = null;
    @Override
    public String request() {
    // TODO Auto-generated method stub
    //在真正需要的時候才能建立真實物件,建立過程可能很慢
    if(real==null){
        real = new DBQuery();
    }//在多執行緒環境下,這裡返回一個虛假類,類似於 Future 模式
        return real.request();
    }
  
}

 public class Main {
    public static void main(String[] args){
        IDBQuery q = new DBQueryProxy(); //使用代裡
        q.request(); //在真正使用時才建立真實物件
    }
}

 動態代理

  動態代理指在執行時動態生成代理類。即,代理類的自己嗎將在執行時生成並載入當前代理的ClassLoader。與靜態處理類相比,動態類不需要為真實主題寫一個形式上完全一樣的封裝類,假如主題介面中的方法很多,為每一個介面寫一個代理方法也很麻煩。如果介面有變動,則真實主題和代理類都要修改,不利於系統維護。使用一些動態代理的生成方法可以在 執行時定製代理類的執行邏輯,從而大大提高系統的靈活性。

public class DBQueryHandler implements InvocationHandler {
    IDBQuery realQuery = null ; //定義主題介面
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(realQuery == null)
            realQuery = new DBQuery();
        return realQuery.request();
    }
    /**
     * 返回代理類的例項
     * @return
     */
    public static IDBQuery createProxy(){
        IDBQuery proxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new DBQueryHandler());
        return proxy;
    }
}

  以上程式碼生成了一個實現了IDBQuery介面的代理類,代理類的內部邏輯由DBQueryHandle決定。

  生成代理類後,由newProxyInstance()方法返回該代理類的一個例項。