設(shè)計模式一代理模式

隨筆記:設(shè)計模式一代理模式
感謝:https://juejin.im/post/5d7c6bc7f265da03f3338254

介紹

代理模式 (Proxy Pattren) 也稱為委托模式,是屬于結(jié)構(gòu)型設(shè)計模式,其重要性不言而喻,相信在看過本篇文章之后會發(fā)現(xiàn)不少設(shè)計模式中都有代理模式的影子。那么何為代理模式?其實代理在我們?nèi)粘I钪胁⒉簧僖姡瑢τ诔绦騿T來說最常接觸的莫過于代理上網(wǎng)了,連上代理服務(wù)器地址,就可以輕松暢游全世界的網(wǎng)絡(luò);總而言之,也許你并不留意,但是代理是無處不在,現(xiàn)實生活如此,我們的 Code 世界里也是如此!既然這樣,我們來探究下代理模式倒是有多普遍。下面首先看下代理模式的定義吧。

定義

為其它對象提供一種代理以控制對這個對象的訪問。

使用場景

當(dāng)無法或不想直接訪問某個對象存在困難時可以通過一個代理對象來間接訪問,為了保證客戶端使用的透明性,委托對象與代理對象需要實現(xiàn)相同的接口。

UML 類圖

image.png
  • Subject: 抽象主題類;該類的主要職責(zé)是聲明真實主題與代理的共同接口方法,該類既可以是一個抽象類也可以是一個接口。
  • RealSubject: 真實主題類;該類也稱為被委托類或被代理類,該類定義了代理所表示的真實對象,由其執(zhí)行具體的業(yè)務(wù)邏輯方法,而客戶類則通過代理類間接地調(diào)用真實主題中定義的方法。
  • ProxySubject: 代理類;該類也稱為委托類或代理類,該類持有一個對真實主題類的引用,在其所實現(xiàn)的接口方法中調(diào)用真實主題類中相應(yīng)的接口方法執(zhí)行,以此起到代理的作用。
  • Client: 客戶類,即使用代理類的類型。

代碼示例

代理模式可以大致分為兩大部分,一是靜態(tài)代理,而是動態(tài)代理。靜態(tài)代理是只有具體的代理對象,而動態(tài)代理則與靜態(tài)代理相反,通過反射機(jī)制動態(tài)地生成代理者的對象,也就是說我們在 code 階段壓根不需要知道代理對象是誰,代理誰將會在執(zhí)行階段決定。下面我們就來看下吧

簡單示例

靜態(tài)代理

業(yè)務(wù)背景: X 程序員在公司上班時,遇見了公司拖欠工資甚至克扣工資的情況,這種情況下 X 程序員還是通過法律途徑來解決問題,這個時候就需要請一個律師來作為自己的訴訟代理人,先看下面的代碼示例吧

訴訟接口類:

public interface ILawsuit {

    /**
     * 提交申請
     */
    void submit();

    /**
     * 舉行舉證
     */
    void burden();

    /**
     * 開始辯護(hù)
     */
    void defend();

    /**
     * 訴訟完成
     */
    void finish();
}
復(fù)制代碼

具體訴訟人(X程序員):

public class XProgrammer implements ILawsuit {
    @Override
    public void submit() {
        System.out.println("老板欠 X 程序員工資,申請仲裁!");

    }

    @Override
    public void burden() {
        System.out.println("這是合同書和過去一年的銀行工資流水");

    }

    @Override
    public void defend() {
        System.out.println("證據(jù)確鑿!不需要再說什么了!");

    }

    @Override
    public void finish() {
        System.out.println("訴訟成功!判決老板即日起 7 天內(nèi)結(jié)算工資!");
    }
}
復(fù)制代碼

X程序員請的律師(代理對象):

public class ProxyLawyer implements ILawsuit {
    /**
     * 持有一個具體被代理者的引用,這里就是 X 程序員,也可以是其它 Y 程序員 只是具體說明。
     */
    private ILawsuit mLawsuit;

    public ProxyLawyer(ILawsuit lawsuit) {
        mLawsuit = lawsuit;
    }

    @Override
    public void submit() {
        mLawsuit.submit();
    }

    @Override
    public void burden() {
        mLawsuit.burden();
    }

    @Override
    public void defend() {
        mLawsuit.defend();
    }

    @Override
    public void finish() {
        mLawsuit.finish();
    }
}
復(fù)制代碼

test:

    @Test
    public void testProxy(){

        //X 程序員
        ILawsuit lawsuit = new XProgrammer();

        //程序員請的律師,把自己的事務(wù)交于律師來處理
        ILawsuit proxyLawyer = new ProxyLawyer(lawsuit);

        //律師開始處理
        proxyLawyer.submit();
        proxyLawyer.burden();
        proxyLawyer.defend();
        proxyLawyer.finish();
    }
復(fù)制代碼

output:

老板欠 X 程序員工資,申請仲裁!
這是合同書和過去一年的銀行工資流水
證據(jù)確鑿!不需要再說什么了!
訴訟成功!判決老板即日起 7 天內(nèi)結(jié)算工資!
復(fù)制代碼

動態(tài)代理

動態(tài)代理其實也很簡單,Java 給我們提供了一個很便捷的動態(tài)代理接口 InvocationHandler ,實現(xiàn)該接口需要重寫其調(diào)用方法 invoke。

下面我們就上面的示例來稍加改動一下:

動態(tài)代理類(這里可以理解為律師對象)

public class DynamicProxy implements InvocationHandler {

    /**
     * 代理者的引用(這里可以理解為 X 程序員)
     */
    private Object object;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //通過反射調(diào)用其代理者的方法
        return method.invoke(object,args);
    }
}
復(fù)制代碼

test:

    @Test
    public void testDynamicProxy(){

        //X 程序員
        ILawsuit lawsuit = new XProgrammer();

        //構(gòu)造一個動態(tài)代理對象(程序員請的律師,把自己的事務(wù)交于律師來處理)
        DynamicProxy dynamicProxy = new DynamicProxy(lawsuit);

        //拿到代理者身上的 ClassLoader
        ClassLoader classLoader = lawsuit.getClass().getClassLoader();

        //動態(tài)的構(gòu)造一個代理者律師出來
        ILawsuit proxyLawyer = (ILawsuit) Proxy.newProxyInstance(classLoader, new Class[]{ILawsuit.class}, dynamicProxy);

        //律師開始處理
        proxyLawyer.submit();
        proxyLawyer.burden();
        proxyLawyer.defend();
        proxyLawyer.finish();
    }
復(fù)制代碼

output:

老板欠 X 程序員工資,申請仲裁!
這是合同書和過去一年的銀行工資流水
證據(jù)確鑿!不需要再說什么了!
訴訟成功!判決老板即日起 7 天內(nèi)結(jié)算工資!
復(fù)制代碼

運行結(jié)果和靜態(tài)代理一樣,由此可見動態(tài)代理通過一個代理類來代理 N 多個被代理類,其本質(zhì)是對代理者與被代理者進(jìn)行解耦,使兩者沒有直接的耦合關(guān)系。相對而言靜態(tài)代理則是能為給定接口下的實現(xiàn)類做代理,如果接口不同那么就需要重新定義不同的代理類,最為復(fù)雜,但是靜態(tài)代理更符合面向?qū)ο笤瓌t。在開發(fā)時具體使用哪種方式來實現(xiàn)代理,就看自己的偏好了。

總結(jié)

代理模式應(yīng)用廣泛,在后面的結(jié)構(gòu)型模式中,你都可以看到代理模式的影子,有些模式單獨作為一種設(shè)計模式,倒不如說是對代理模式的一種針對性優(yōu)化。而且代理模式幾乎沒有缺點可言,它是細(xì)分化至很小的一種模式,如果非得說一個缺點的話,那么就是設(shè)計模式的通病,對類的增加。不過在這種孰優(yōu)孰劣的局勢下,就算對類的稍微增加又有什么問題呢,是吧?

作者:DevYK
鏈接:https://juejin.im/post/5d7c6bc7f265da03f3338254
來源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容