代理模式

定義

代理模式(Proxy Pattern):給某一個(gè)對(duì)象提供一個(gè)代理或者占位符,并由代理對(duì)象控制對(duì)原對(duì)象的訪問(wèn)。

我的理解是代理模式是對(duì)真實(shí)對(duì)象行為的訪問(wèn)控制,而非對(duì)其本身行為的加強(qiáng),這樣既能保證真實(shí)對(duì)象的功能純凈性,又允許對(duì)其功能流程進(jìn)行修改,同時(shí)能保證對(duì)客戶(hù)端透明。而包裝模式則是對(duì)真實(shí)對(duì)象行為的加強(qiáng)。比如拳擊手與其經(jīng)紀(jì)人的關(guān)系是代理關(guān)系,而拳擊手與其拳擊手套的關(guān)系則偏重于包裝關(guān)系。

實(shí)例

public interface IProxy<T> {
    public T newProxy(T t);
}
public interface ILoginService {
    public void init(Context context);
    public void auth();
    public void unAuth();
}
public class ILoginServiceProxy implements IProxy<ILoginService>, ILoginService{
    ILoginService mLoginService;

    @Override
    public ILoginService newProxy(ILoginService loginServiceImpl) {
        mLoginService = loginServiceImpl;
        return this;
    }

    @Override
    public void init(Context context) {
        if (mLoginService != null) {
            mLoginService.init(context);
        }
    }

    @Override
    public void auth() {
        if (mLoginService != null) {
            mLoginService.auth();
        }
    }

    @Override
    public void unAuth() {
        if (mLoginService != null) {
            mLoginService.unAuth();
        }
    }
}

上述的業(yè)務(wù)邏輯是登錄/注銷(xiāo)操作,一般我們登錄成功后都會(huì)將用戶(hù)數(shù)據(jù)存儲(chǔ)到本地,注銷(xiāo)時(shí)都會(huì)將部分用戶(hù)數(shù)據(jù)銷(xiāo)毀或者標(biāo)記為失效。這個(gè)時(shí)候我們?cè)趺刺幚砟??如果我們修改真?shí)的登錄服務(wù)提供者,那么該服務(wù)就不純潔,因?yàn)轳詈狭藬?shù)據(jù)庫(kù)操作和業(yè)務(wù)操作,如果現(xiàn)在馬甲包需要登錄功能,此時(shí)數(shù)據(jù)庫(kù)表結(jié)構(gòu)不一致就需要修改代碼。所以這個(gè)時(shí)候我們可以修改登錄功能代理類(lèi)。在登錄后,注銷(xiāo)后都插入需要執(zhí)行的邏輯。

public class DynamicProxy<T> {
   /*
     * Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
     * 方法的第一個(gè)參數(shù)的作用就是 獲取當(dāng)前類(lèi)的類(lèi)加載器,作用是用來(lái)生成類(lèi)的
     * 第三個(gè)參數(shù)是獲取真實(shí)對(duì)象的所有接口    獲取所有接口的目的是用來(lái)生成代理的,因?yàn)榇硪獙?shí)現(xiàn)所有的接口
     * 第二個(gè)參數(shù)是 調(diào)用處理器  這里傳入調(diào)用處理器,是因?yàn)樯纱韺?shí)例需要 調(diào)用處理器    為什么需要調(diào)用處理器,因?yàn)樯傻拇聿荒苤苯诱{(diào)用真實(shí)對(duì)象的方法,
     * 而是通過(guò)調(diào)用處理器來(lái)調(diào)用真實(shí)對(duì)象的方法,具體就是通過(guò)上面定義的DynamicProxyHandler重寫(xiě)父類(lèi)InvocationHandler的invoke方法
     */
    public static <T> T newProxyInstance(ClassLoader classLoader, InvocationHandler handler, Class<?>[] classes){
        return (T) Proxy.newProxyInstance(classLoader, classes, handler);
    }
}
public class MyInvocationHandler implements InvocationHandler {
    Object target = null;

    MyInvocationHandler(Object o){
        this.target = o;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
       //可以在這里插入一些事務(wù)處理,
        return method.invoke(target, objects);
    }
}

newProxyInstance方法會(huì)動(dòng)態(tài)生成一個(gè)代理對(duì)象,客戶(hù)端只需要使用該代理對(duì)象就行。本質(zhì)上,newProxyInstance方法會(huì)在運(yùn)行時(shí)動(dòng)態(tài)生成一個(gè)代理類(lèi)的字節(jié)碼,當(dāng)客戶(hù)端調(diào)用代理對(duì)象某個(gè)方法時(shí),就是調(diào)用生成的這個(gè)字節(jié)碼類(lèi)對(duì)應(yīng)的對(duì)象的方法,而該方法又委托了InvocationHandler的invoke方法去執(zhí)行,即method.invoke(target, objects)方法才是通過(guò)反射去執(zhí)行被代理對(duì)象的方法。
可以發(fā)現(xiàn)動(dòng)態(tài)代理中的InvocationHandler接口,將被代理對(duì)象的方法執(zhí)行開(kāi)放處理,是我們可以對(duì)任意類(lèi)中的任意方法做切片處理,而且InvocationHandler接口的實(shí)現(xiàn)者可以以功能劃分和重用,比如我們需要統(tǒng)計(jì)類(lèi)中方法耗時(shí),可以直接寫(xiě)一個(gè)耗時(shí)統(tǒng)計(jì)的InvocationHandler實(shí)現(xiàn),在method.invoke(target, objects);方法前后插入耗時(shí)統(tǒng)計(jì)。

代理模式的應(yīng)用場(chǎng)合

代理模式有多種應(yīng)用場(chǎng)合,如下所述:

遠(yuǎn)程代理,也就是為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí)。比如說(shuō) WebService,當(dāng)我們?cè)趹?yīng)用程序的項(xiàng)目中加入一個(gè) Web 引用,引用一個(gè) WebService,此時(shí)會(huì)在項(xiàng)目中聲稱(chēng)一個(gè) WebReference 的文件夾和一些文件,這個(gè)就是起代理作用的,這樣可以讓那個(gè)客戶(hù)端程序調(diào)用代理解決遠(yuǎn)程訪問(wèn)的問(wèn)題;

虛擬代理,是根據(jù)需要?jiǎng)?chuàng)建開(kāi)銷(xiāo)很大的對(duì)象,通過(guò)它來(lái)存放實(shí)例化需要很長(zhǎng)時(shí)間的真實(shí)對(duì)象。這樣就可以達(dá)到性能的最優(yōu)化,比如打開(kāi)一個(gè)網(wǎng)頁(yè),這個(gè)網(wǎng)頁(yè)里面包含了大量的文字和圖片,但我們可以很快看到文字,但是圖片卻是一張一張地下載后才能看到,那些未打開(kāi)的圖片框,就是通過(guò)虛擬代里來(lái)替換了真實(shí)的圖片,此時(shí)代理存儲(chǔ)了真實(shí)圖片的路徑和尺寸;

安全代理,用來(lái)控制真實(shí)對(duì)象訪問(wèn)時(shí)的權(quán)限。一般用于對(duì)象應(yīng)該有不同的訪問(wèn)權(quán)限的時(shí)候;

指針引用,是指當(dāng)調(diào)用真實(shí)的對(duì)象時(shí),代理處理另外一些事。比如計(jì)算真實(shí)對(duì)象的引用次數(shù),這樣當(dāng)該對(duì)象沒(méi)有引用時(shí),可以自動(dòng)釋放它,或當(dāng)?shù)谝淮我靡粋€(gè)持久對(duì)象時(shí),將它裝入內(nèi)存,或是在訪問(wèn)一個(gè)實(shí)際對(duì)象前,檢查是否已經(jīng)釋放它,以確保其他對(duì)象不能改變它。這些都是通過(guò)代理在訪問(wèn)一個(gè)對(duì)象時(shí)附加一些內(nèi)務(wù)處理;

延遲加載,用代理模式實(shí)現(xiàn)延遲加載的一個(gè)經(jīng)典應(yīng)用就在 Hibernate 框架里面。當(dāng) Hibernate 加載實(shí)體 bean 時(shí),并不會(huì)一次性將數(shù)據(jù)庫(kù)所有的數(shù)據(jù)都裝載。默認(rèn)情況下,它會(huì)采取延遲加載的機(jī)制,以提高系統(tǒng)的性能。Hibernate 中的延遲加載主要分為屬性的延遲加載和關(guān)聯(lián)表的延時(shí)加載兩類(lèi)。實(shí)現(xiàn)原理是使用代理攔截原有的 getter 方法,在真正使用對(duì)象數(shù)據(jù)時(shí)才去數(shù)據(jù)庫(kù)或者其他第三方組件加載實(shí)際的數(shù)據(jù),從而提升系統(tǒng)性能。

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

相關(guān)閱讀更多精彩內(nèi)容

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