單例和代理模式(Java)

設計模式之單例和代理模式(Java)


  1. 單例模式

    • 核心:只能獲取類的同一個實例。

    • 實例:應用單例模式,設計SingletonObject類並進行測試。

    • 關鍵點:

      1. 構造函數爲私有;
      2. 代表該類單實例的引用爲靜態,初始化爲null;
      3. 獲取單實例的方法爲公開靜態,並且當單實例引用屬性爲null時重新構造新實例,否則直接返回該引用,確保只有一個實例被返回。
    • 設計:

      public class SingletonObject {
      
          private static SingletonObject instance;
      
          private SingletonObject(){
              System.out.println("Created SingletonObject instance.");
          }
      
          public static SingletonObject getInstance() {
              if(instance == null) {
                  instance = new SingletonObject();
              }
              return instance;
          }
      
      }
      
    • 測試:

      public static void main(String[] args) {
          SingletonObject.getInstance();
          SingletonObject.getInstance();
          SingletonObject.getInstance();
      }
      
    • 運行結果:

      Created SingletonObject instance.
      
      

      觀察到,SingletonObject構造方法只調用了一次。

  2. 代理模式

    • 核心:

      1. 延遲加載

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

        使用代理模式的延遲加載是非常有意義的,首先,它可以在時間軸上分散系統壓力,尤其在系統啓動時,不必完成所有的初始化工作,從而加速啓動時間;其次,對很多真實主題而言,在軟件啓動直到被關閉的整個過程中,可能根本不會被調用,初始化這些數據無疑是一種資源浪費。例如使用代理類封裝數據庫查詢類後,系統的啓動過程這個例子。若系統不使用代理模式,則在啓動時就要初始化 DBQuery 對象,而使用代理模式後,啓動時只需要初始化一個輕量級的對象 DBQueryProxy。

      2. 動態代理

        動態代理是指在運行時動態生成代理類。即,代理類的字節碼將在運行時生成並載入當前代理的 ClassLoader。

        與靜態處理類相比,動態類有諸多好處。首先,不需要爲真實主題寫一個形式上完全一樣的封裝類,假如主題接口中的方法很多,爲每一個接口寫一個代理方法也很麻煩。如果接口有變動,則真實主題和代理類都要修改,不利於系統維護;其次,使用一些動態代理的生成方法甚至可以在運行時制定代理類的執行邏輯,從而大大提升系統的靈活性。

        (因爲原網站說的比較深,就直接引用過來了,原文連接在此)

    • 實例:設計一個數據查詢代理類,代理真實的數據庫查詢對象進行查詢操作。

    • 思想:

      1. 延遲加載
        1. 設計數據庫查詢公共接口
        2. 設計真實的數據庫查詢類
        3. 設計數據庫查詢代理類;代理類實例化時不實例化真實的數據庫查詢類,而是在代理類執行查詢操作時才進行實例化。
      2. 動態代理
        1. 設計數據庫查詢公共接口
        2. 設計真實的數據庫查詢類
        3. 設計用於動態創建代理類的InvocationHandler,並使用Proxy.newProxyInstance()方法獲取代理類實例
    • 設計:

      1. 延遲加載

        interface IDBQuery:

        public interface IDBQuery {
            String request();
        }
        

        class DBQuery:

        public class DBQuery implements IDBQuery{
        
            public DBQuery() {
                try {
                    Thread.sleep(500);  //模擬初始化時消耗資源需要等待一定時間
                    System.out.println("數據庫連接初始化完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        
            @Override
            public String request() {
                return "result from DBQuery";
            }
        }
        

        class ProxyDBQuery:

        public class ProxyDBQuery implements IDBQuery{
            private DBQuery realDBQuery = null;
            @Override
            public String request() {
                if(realDBQuery == null) {
                    realDBQuery = new DBQuery();
                }
                return realDBQuery.request();
            }
        }
        
      2. 動態代理

        interface IDBQuery:(同上)

        class DBQuery:(同上)

        class DBQueryHandler:

        public class DBQueryHandler implements InvocationHandler{
            private DBQuery realDBQuery = null;
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if(realDBQuery == null) {
                    realDBQuery = new DBQuery();
                }
                if(method.getName().equals("request")) {
                    return realDBQuery.request();
                } else {
                    return null;
                }
            }
            public static IDBQuery createProxy() {
                IDBQuery proxy = (IDBQuery) Proxy.newProxyInstance(
                        ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new DBQueryHandler()
                );
                return proxy;
            }
        }
        
    • 測試:

      1. 延遲加載:

        代理實例創建完畢
        數據庫連接初始化完成
        代理實例第一次查詢結束
        代理實例第二次查詢結束
        

        可以看到,代理實例創建時沒有進行數據庫連接操作,只有當第一次調用查詢時才進行數據庫連接操作,這樣加快系統啓動速度,避免在用戶並不需要查詢數據時造成的資源浪費。

      2. 動態代理:

        動態代理類實例創建完成
        數據庫連接初始化完成
        代理實例第一次請求
        代理實例第二次請求
        

        動態代理結果和延遲加載類似。動態代理的好處在於不需要寫一個和接口形式完全一樣的封裝類,減少代碼冗餘,並且可以在運行時動態制定代理類的運行邏輯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章