【java設計模式】之 代理(Proxy)模式(上)

先來看下代理模式的定義和目的  
        定義:給目標對象提供一個代理對象,並由代理對象控制對目標對象的引用
        目的:1、通過引入代理對象的方式來簡介訪問目標對象,防止直接訪問目標對象給系統帶來不必要的複雜性
                   2、通過代理對象對原有的業務增強

好吧,這看上去完全不像人話,那我舉個例子把這個說成人話把。如下:

       張三現在想點東西,給生活帶來一點激情,這類好東西呢一般都在國外那邊的某些公司纔有生成。如果張三直接跑過去買,不僅要購買飛機票,跑來跑去,而且去了也不會說外國話,萬一別人理解錯了就麻煩大了。而且啊,買到了你要自己帶着這麼刺激的東西會不會很緊張,被發現了多尷尬。這搞下來不得麻煩死,所以乾脆找代理。啥都不用管,只需要把需求給代理講清楚,代理做專業的市場調研給你找最好的貨,買到了給你提供精美的包裝海外快遞過來一條龍服務並且之後有問題隨時諮詢,算不算增強了你原本的需求。代理模式給我們帶來了很多便捷。

       上圖基本邏輯如上圖。在程序中也是如此,通過代理,可以詳細控制訪問某個或者某類對象的方法,在調用這個方法前做前置處理,調用這個方法後做後置處理。這也是AOP的實現原理。
  那麼代理模式的核心角色該如何設計呢?

1、靜態代理

根據上面的實現步驟,首先來寫一個抽象角色:

/***
 * 抽象類:某激情公司接口
 */
public interface  PassionCompany {
    public void  saleManPassion(String type);   //產品尺寸
}
這個抽象類中有一個方法,現在分別讓真實角色和代理角色來實現該抽象類:
/***
 * 真實對象,提供張三需要的產品的公司
 */
public class ManPassionCompany implements PassionCompany {
    @Override
    public void saleManPassion(String type){
        System.out.println("售賣產品尺寸"+type);
    }
}

真實對象繼承抽象類,重寫方法

/***
 * 代理類,集成接口並將真是類包含進來,然後對真實對象增強,提供更好的售前售後服務
 */
public class PassionProxy implements PassionCompany {
    private ManPassionCompany manPassionCompany;  
    public PassionProxy(ManPassionCompany manPassionCompany) { //將真實對象包含進來
        this.manPassionCompany = manPassionCompany;
    }

    @Override
    public void saleManPassion(String type){
        preSales();
        manPassionCompany.saleManPassion(type);
        afterSales();
    }

    public  void preSales(){
        System.out.println("代理提供一品的售前服務");
    }

    public  void afterSales(){
        System.out.println("代理提供售後的一條龍服務");
    }
}
真實對象和代理對象都寫好了,代理對象中我們對真實對象進行了額外的相關服務,有點類似裝飾者模式。
/***
 * 測試類
 */
public class ProxyClient {
    public static void main(String[] args) {
        ManPassionCompany passionCompany= new ManPassionCompany();
        PassionProxy passionProxy= new PassionProxy(passionCompany);
        passionProxy.saleManPassion("3");
    }
}

輸出結果:

  1. 代理提供一品的售前服務
  2. 售賣產品尺寸3
  3. 代理提供售後的一條龍服務

可以看出,張三隻跟代理對象打交道,代理對象把能做的都做了,比如購買激情產品之前的調研和售後打包一條龍服務,只是把去真實對象採購就闊以了。

 

2. 動態代理

        靜態代理講完了,那麼接下來就是動態代理。首先需要思考一個問題,有了靜態代理了之後爲什麼需要動態代理。

        來來來,我們在靜態代理的基礎上思考一下,現在只是張三要買東西,萬一以後張三的老婆要買女性,李四買動漫、王五買化妝品也找那個代理,或者張三哪天又想買非type類型的產品要來個sex類型的,那麼真實的類中的裏面是不是業務更加複雜了,還要去修改裏面的代碼,繼承更多的接口,可維護性差擴展性也不好,不符合代理模式的基本準備——開閉準則。那麼應該怎麼完美的解決這個問題呢,既然有專門做激情代理的,代理的圈子內各有不同的專業,整合到一起成立一個代理集合,這樣每次根據客戶需求安排相應的代理去完成。注意一下,這裏怎麼安排的呢。其實就是核心機制就是反射機制

代理模式圖變成如下:(本質沒有變化)

這裏lison代理公司的核心機制是什麼:

  1. Proxy:根據代購需求調用公司具備改代購需求專業的員工去做。——>通過newProxyInstance生成動態代理對象
  2. InvocationHandler:是個接口,只有一個invoke()方法,就是對業務增強的方法。

     遵循代理基本原則單一職責原則,某個類只負責一個業務能力。職責劃分非常清晰。

再來根據上圖邏輯我們來寫代碼。

首先我們需要註冊一個代理公司,不同的需求可以分配不同專業方向的員工去做。

public class PassionProxyCompany implements InvocationHandler {
    //被代理的對象
    private Object company;

    public Object getCompany() {
        return company;
    }

    public void setCompany(Object company) {
        this.company = company;
    }

    //通過Proxy獲取動態代理的對象
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(company.getClass().getClassLoader(),company.getClass().getInterfaces(),this);
    }


    public Object invoke(Object proxy, Method method,Object[] args) throws InvocationTargetException, IllegalAccessException {
        // 在代理真實對象前,增強業務
        doSomeThingBefore();
        //這裏要進行動態代理不知道調用那個對象,所以將method對象的方法作爲參數傳入,根據反射調用要優化的方法。
        Object ret = method.invoke(company, args);
        // 在代理真實對象之後,增強業務
        doSomeThingAfter();
        return ret;
    }

    public void doSomeThingBefore(){
        System.out.println("根據需求,進行市場調研和產品分析");
    }

    public void doSomeThingAfter(){
        System.out.println("精美包裝,一條龍服務");
    }

PassionCompany裏面包含核心業務,一個是根據需求分配員工Proxy,第二個是增強的業務invoke();

然後我們測試一下: 注意這裏生成的真實類是父類引用指向子類對象

public class dtProxy {
    public static void main(String[] args) {
       //生成男性用品
        APassionCompany man = new ManPassionCompany();
        //生成女性用品
        BPassionCompany woman = new WomanPassionCompany();
        //代理公司
        PassionProxyCompany passionProxyCompany = new PassionProxyCompany();

        //張三來找我,購買男用品
        passionProxyCompany.setCompany(man);
        //分配給專門的1號員工去做
        APassionCompany lison1 = (APassionCompany)passionProxyCompany.getProxyInstance();
        lison1.saleManPassion("D");
        System.out.println("-------------------------");

        //張三老婆來找我,購買女性用品
        passionProxyCompany.setCompany(woman);
        //分配給專門的2號員工去做
        BPassionCompany lison2 = (BPassionCompany)passionProxyCompany.getProxyInstance();
        lison2.saleWomanPassion(18);
    }
}

輸出結果爲:

  1. 根據需求,進行市場調研和產品分析
  2. 售賣產品尺寸D
  3. 精美包裝,一條龍服務
  4. -------------------------
  5. 根據需求,進行市場調研和產品分析
  6. 售賣產品尺寸18
  7. 精美包裝,一條龍服務

由此可見,使用代理可以在執行某個邏輯的前後加上新的邏輯,這是很好的功能,實際中Spring的AOP用的就是這種技術。

下篇會接着說,proxy裏面到底做了什麼。

 

 

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