1. 程式人生 > >一個小姐姐引發的java代理模式

一個小姐姐引發的java代理模式

java的代理模式是很常見的一種設計模式,記錄一下我的理解。

代理模式主要的思想是將委託類(被代理類)和真正使用委託類邏輯的類做一個解耦。

太官方的原理我也說不好,所以還是說說自己的理解好了。

java的代理模式分兩種:

  1. 靜態代理
  2. 動態代理

我個人認為,如果要理解這種思想,還是主要要搞懂靜態代理,這樣才能更好地理解這種模式,而動態代理則是呼叫java的Proxy這個API對靜態代理做的一種更靈活的實現吧。

首先,我們以生活場景來說。
假設現在我得朋友圈裡有一個做微商的小姐姐分享了一個我喜歡的鞋子我很喜歡,我想買這時直接從她那邊下單。她肯定不是自己生產的呀,她手上肯定有一個廠家,我在她這裡下單,她直接向廠家下單,甚至貨物都直接從廠家發給我,而我並不知道她背後的廠家是誰,不知道他們是怎麼生產的,也不知道廠家定價是多少錢。這時我和廠家直接就是解耦的,也就是說廠家如果要調整價格,那麼廠家價格調整之後真正到我手上的價格就是看代理姐姐心情了,我也不知道。這時候角色就定了:廠家(被代理類),小姐姐(代理類),我(我就是我咯)

靜態代理

思路就是這麼個思路,程式碼的實現我們這就開始:
我們抽象出一個Business類,這個類是不管廠商還是代理都需要的一些公共的特徵。比如廠家和小姐姐他們的公共特徵那就是賣貨,這是他們真正的目的:

package com.example.builderdemo.proxy.staticProxy;

// 這是一個生意抽象出來的類,等於是一些公共的東西,比如做生意就是賣貨,賣服務等等這種公共行為
// 不管是廠家還是代理商都要做的一些事情
public interface Business {
    long sell();
}

接下來,我們需要把廠商定義出來,我想定義一個國產的廠商,來個安踏吧:

package com.example.builderdemo.proxy.dynmaicProxy;

import com.example.builderdemo.LogUtils;

// 這是廠家類,也就是一個實現了Business類介面的實現類
public class Anta implements Business {

    // 廠家可以具有某些屬性,比如生產貨物,定價等等
    // 以此為例的話,其實不一定所有的Business都有生產貨物的能力
    // 要表達的也只是這些能力並不用被消費者所知道,所以這個對消費者來說是透明的,他們不用關心
    private String cargo = "鞋子";// 廠家生產的貨物
    private long price = 100;// 廠家的定價

    @Override
    public long sell() {
        LogUtils.e("I am a menufacturers, i sell my cargo:" + cargo);
        return price;
    }
}

廠商肯定具備賣貨的特徵,這就是我們剛剛抽象出來的介面,所以我們實現Business介面。

接下來得把代理商也就是小姐姐定義出來,代理商同樣是要賣貨,所以我們也要實現Business介面:

package com.example.builderdemo.proxy.staticProxy;

// 這是一個代理類,這個類代理廠家的貿易,幫廠家進貨。
// 代理商也是以做生意賣貨為生,所以也是一個Business,是個商人,也具備賣貨的屬性
// 但是
public class Agent implements Business {

    // 每一個代理商手頭必須有一個廠家吧,不然他們的貨從何而來啊
    // 代理商其實就是賺中間的差價,所以如果他黑心一點他就翻倍加價,或者有的代理商熱衷慈善他一分錢不賺
    Anta anta= null;

    // 建構函式中將真正要被代理的類傳入,也是一個最簡單的依賴注入,這裡不展開說
    // 現實中也很好理解,以此為例的話就是你要做一個代理商,你必須要代理某一個廠家的產品吧
    public Agent(Anta anta) {
        this.anta = anta;
    }

    @Override
    public long sell() {

        return anta.sell();// 很顯然,我們碰上好人了,一分錢價格都沒漲,直接賣了

        // 但是,現實中往往最缺的就是這種人,如果不加價他怎麼養活自己呢,所以我們看到有些較黑心的商人他是這樣子的
        // long price = anta.sell();
        // return price + 100;// 但是不可否認,對於經商來說他很成功,他是個合格的代理
    }
}

這樣子就滿足了java代理模式的三個要素:

  1. 介面
  2. 實現介面的委託類,也就是被代理的類。
  3. 代理類

那麼我們怎麼買這個鞋子呢?

public void byShooes() {
	Anta anta = new Anta();
	Agent agent = new Agent(anta);
	long price = agent.sell();
	LogUtils.e("The shooes  is " + price+ " yuan!");
	// 到此為止,我們的顧客成功買到了想要的鞋子
}

至此,靜態代理搞定了。代理的思想不知道有沒有被我曲解,但是我個人是這麼理解的。
總的來說,就是:

  1. 建立一個抽象介面
  2. 建立抽象介面的真正實現類來實現該抽象介面,也就是委託類(被代理類)
  3. 建立代理類,代理類也要實現該抽象介面,這樣主要是為了能和介面有一樣的方法,讓使用者能無障礙的直接呼叫相同的方法,感覺就像是呼叫的就是委託類(被代理類)一樣。
  4. 在代理類中持有委託類(被代理類)的引用。
  5. 在抽象介面實現過來的方法中直接呼叫委託類(被代理類)的相對應方法。

但是,從上面你有沒有想過,要是我想要特步怎麼辦,小姐姐這邊沒有代理他們家的鞋子,那我就得到別處去買了。怎麼可能,小姐姐多精啊,能吃這虧?

package com.example.builderdemo.proxy.staticProxy;

import com.example.builderdemo.LogUtils;

// 這是廠家特步的類,也就是一個實現了Business類介面的實現類
public class Tebu implements Business {
    private String cargo = "鞋子";// 廠家生產的貨物
    private long price = 110;// 廠家的定價

    @Override
    public long sell() {
        LogUtils.e("I am a menufacturers, i sell my cargo:" + cargo);
        return price;
    }
}

接著我們的小姐姐要擴充套件業務了:

package com.example.builderdemo.proxy.staticProxy;

// 這是一個代理類,這個類代理廠家的貿易,幫廠家進貨。
// 代理商也是以做生意賣貨為生,所以也是一個Business,是個商人,也具備賣貨的屬性
// 但是
public class Agent implements Business {

    // 每一個代理商手頭必須有一個廠家吧,不然他們的貨從何而來啊
    // 代理商其實就是賺中間的差價,所以如果他黑心一點他就翻倍加價,或者有的代理商熱衷慈善他一分錢不賺
    Business shooes= null;

    // 建構函式中將真正要被代理的類傳入,也是一個最簡單的依賴注入,這裡不展開說
    // 現實中也很好理解,以此為例的話就是你要做一個代理商,你必須要代理某一個廠家的產品吧
    public Agent(Business shooes) {
        this.shooes = shooes;
    }


    @Override
    public long sell() {

        return shooes.sell();// 很顯然,我們碰上好人了,一分錢價格都沒漲,直接賣了

        // 但是,現實中往往最缺的就是這種人,如果不加價他怎麼養活自己呢,所以我們看到有些較黑心的商人他是這樣子的
        // long price = shooes.sell();
        // return price + 100;// 但是不可否認,對於經商來說他很成功,他是個合格的代理
    }
}

我們把介面傳遞進來,那麼這時候不管你什麼廠家,管你安踏李寧特步鴻星爾克,通通搞定。

臃腫的靜態代理

但是我們又考慮到,不可能客戶直接就去找小姐姐買鞋呀,小姐姐得天天打廣告,晒截圖我們才能選擇相信她呀,不能就憑她長得好看就去,這是原則問題。那問題就來了,小姐姐還得打廣告,還得晒截圖,要是發展著發展著小姐姐想多賺點還得做回訪呀,還得做活動呀,各種事情,那麼是不是每一種操作都得做一遍:

package com.example.builderdemo.proxy.staticProxy;

// 這是一個代理類,這個類代理廠家的貿易,幫廠家進貨。
// 代理商也是以做生意賣貨為生,所以也是一個Business,是個商人,也具備賣貨的屬性
// 但是
public class Agent implements Business {

    // 每一個代理商手頭必須有一個廠家吧,不然他們的貨從何而來啊
    // 代理商其實就是賺中間的差價,所以如果他黑心一點他就翻倍加價,或者有的代理商熱衷慈善他一分錢不賺
    Business shooes = null;

    // 建構函式中將真正要被代理的類傳入,也是一個最簡單的依賴注入,這裡不展開說
    // 現實中也很好理解,以此為例的話就是你要做一個代理商,你必須要代理某一個廠家的產品吧
    public Agent(Business shooes) {
        this.shooes = shooes;
    }


    @Override
    public long sell() {

        return shooes.sell();// 很顯然,我們碰上好人了,一分錢價格都沒漲,直接賣了

        // 但是,現實中往往最缺的就是這種人,如果不加價他怎麼養活自己呢,所以我們看到有些較黑心的商人他是這樣子的
        // long price = shooes.sell();
        // return price + 100;// 但是不可否認,對於經商來說他很成功,他是個合格的代理
    }
    @Override
    public void ad() {

        anta.ad();
    }

    @Override
    public long show() {

        return anta.show();
    }
    @Override
    public long event() {

        return anta.event();
    }
	...
}

那是不是隻要介面加入一個方法我們在委託類也實現這個方法,那代理類也必須得加入這個方法,那麼這時候廠家做出各種各樣的活動,那麼小姐姐就算咱們什麼都不改,廠家做什麼活動咱照著做什麼活動,廠家說打九折咱也不說打九五折了,直接打九折的話代理類也得變呀。那程式碼是不是又臭又長,裡面會有各種的方法。這時候,動態代理出現了

動態代理

動態代理在java中很常見,動態代理與靜態代理的區別在於,代理類只需要一個方法就ok,不管你後面廠家出什麼么蛾子,代理類都能通過反射執行對應的方法。

這時我們只需要改變代理類的實現即可,先上程式碼,後面再來解釋:

public class DynmaicAgent implements InvocationHandler {

    Object object;

    public DynmaicAgent(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object, args);
        if (method.getName().equals("sell")) {
            return (long) result + 100;
        }
        return result;
    }
}

從上面看到,我們的代理類需要實現一個叫InvocationHandler的介面,這個介面是jdk中定義的API,裡面只有一個方法:

public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

根據API中的解釋,我嘗試著解釋了一下這幾個引數的含義:

  1. poxy:委託類(被代理類)的例項
  2. 委託類(被代理類)被呼叫到的方法
  3. “2”中方法所具備的引數

注意:invoke方法的返回值是一個Object,這個方法的返回值既可以是委託類的例項,也可以是執行方法的返回值

同樣的,代理類也持有一個委託類(被代理類)的引用。(我感覺這步驟可以不用,而直接使用invoke的proxy引數)

這時候當我要買鞋子的時候:

public void byShooesDynmaic() {
        Business anta = new Anta();
        DynmaicAgent agent = new DynmaicAgent(anta);
        ClassLoader classLoader = anta.getClass().getClassLoader();
        Business business = (Business) Proxy.newProxyInstance(classLoader, new Class[]{Business.class}, agent);
        long price = business.sell();
        LogUtils.e("The shooes is " + price + " yuan!");
    }

Proxy:
在這裡插入圖片描述

Proxy類也是jdk裡面提供的API,API中對該類的解釋是:
這個類提供了靜態方法用來建立一個動態代理的和例項,並且它也是所有被該方法建立的所有
的動態代理的的父類。

我們這裡常用的方法就是這個Proxy.newProxyInstance,看看API的解釋:
在這裡插入圖片描述

這個方法的作用下面有解釋:返回一個指定介面的代理的例項,並且該例項呼叫執行的方法會
分發給對應的invocationHandler去執行。
這裡我們的理解是返回一個委託類(被代理類)的例項,並且該類呼叫的方法會被分發到
引數h的invoke去執行。

接下來我們回到剛才的程式碼中:

public void byShooesDynmaic() {
        Business anta = new Anta();
        DynamicAgent agent = new DynamicAgent(anta);
        ClassLoader classLoader = anta.getClass().getClassLoader();
        Business business = (Business) Proxy.newProxyInstance(classLoader, anta.getClass().getInterfaces(), agent);
        long price = business.sell();
        LogUtils.e("The shooes is " + price + " yuan!");
    }

我們來分析一下上面這段程式碼:

  • 首先,我們建立了一個Business介面型別的變數,並且運用java的多型實際建立的是Anta型別的例項。
  • 其次我們建立一個DynamicAgent例項,DynamicAgent的構造傳入的是一個Object型別引數。如果這時候我們想要說讓DynamicAgent的邏輯更清晰一點,說我們這個動態代理只代理Business這個介面,那麼我們傳遞個Business的實現類做實參這就符合需求了。
  • 接著要準備一個委託類(被代理類)的類載入器,anta.getClass().getClassLoader()
  • 緊接著獲取委託類(被代理類)實現的所有介面。
  • 代理類我們已經實現好了,直接傳入
  • 我們可以看到Proxy.newProxyInstance返回的就是一個委託類(被代理類)的例項,所以我們接下來要做什麼操作直接做就行了

當然,我們的動態代理類中的invoke方法可以返回任何能當做返回值的型別,只要我們在內部做好判斷就好了,比如:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object, args);
        if (method.getName().equals("sell")) {
            return (long) result + 100;
        }
        return result;
    }

這個invoke中,我們判斷如果是sell方法,那麼long型別資料再加上100塊,如果我們還有一些別的方法比如getName返回字串,那就是返回字串,比如getAnta我們要把整個委託類(被代理類)引用返回,那也ok。

至此,動態代理記錄完畢

看過兩個文章,人家講的應該更好一點,記錄下來下回可以複習:
https://blog.csdn.net/yaomingyang/article/details/80981004
https://blog.csdn.net/yaomingyang/article/details/81040390

動態代理和裝飾者模式經常會搞混了,這兩天再理解一下裝飾者,裝飾者和靜態代理那邊我認
為主要就是差別在sell方法內部,代理模式只是對一些邏輯增加一些功能或者判斷什麼的,
而裝飾者是一層一層得往委託類(被代理類)內呼叫,並且每個結果又依據委託類(被代理類
)的結果一層一層地裝飾。