淺學設計模式之代理模式(20/23)

代理模式是我們在看源碼中經常會看到的設計模式,像 RetrofitActivityManagerService等都使用到了代理模式。
那什麼時候需要使用到代理模式呢?

1. 代理模式的概念

代理模式(Proxy), 爲其他對象提供一種代理以控制對這個對象的訪問。

結構型的設計模式,在所有的結構型的設計模式中,都會有不少代理模式的影子,代理模式是爲了形成中介隔離,這裏有點像體現迪米特法則的 中介者模式,但是他們還是有一些區別的,這一點學完就知道了。

2 UML圖

來看下UML類圖:
在這裏插入圖片描述
代理模式的結構是非常簡單的。來看下他們的實現。

Subject類,定義了被代理類和代理類的共用接口,這樣就在任何使用RealSubject的地方都可以使用 Proxy類:

public abstract class Subject {
    public abstract void request();
}

其次是 RealSubject類,它是真實邏輯所在的實體:

class RealSubject extends Subject {
    
    @Override
    public void request() {
        System.out.println("真實的請求");
    }
}

最後是 Proxy 代理類,保存一個引用使得代理可以訪問實體,並提供一個與Subject的接口相同的接口,這樣代理就可以用來替代實體:

public class Proxy extends Subject {
    RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        realSubject.request();
    }
}

最後在客戶端代碼中使用代理類來做:

    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }

3. 靜態代理和動態代理

在Java中,代理模式可以分爲靜態代理動態代理

3. 1 靜態代理

靜態代理就和上面的模板代碼一樣,代理者的代碼由程序員自己或通過一些自動化工具生成固定的代碼再對其進行編譯,也就是說我們在代碼運行前,代理類的class編譯文件就已經存在。

3.2 動態代理

動態代理和靜態代理相反,通過反射機制動態地生成代理者對象,也就說在Coding階段根本就不需要知道代理誰,代理誰我們會在執行階段決定,而Java也給我們提供了一個便捷的動態代理接口 InvocationHandler,實現該接口需要重寫其調用方法 invoke():

class DynamicProxy implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

在我們使用代理時,需要對它進行些完善:

class DynamicProxy implements InvocationHandler {
    // 被代理類的引用
    private Object obj;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //調用被代理對象的方法
        return method.invoke(obj, args);
    }
}

這時,再來看看客戶端:

    public static void main(String[] args) {
        // 構造一個實體類
        Subject subject = new RealSubject();

        // 構造一個動態代理
        DynamicProxy proxy = new DynamicProxy(subject);

        // 獲取被代理類RealSubject的ClassLoader
        ClassLoader loader = subject.getClass().getClassLoader();

        // 根據動態代理構造一個代理對象
        Subject proxySubject = (Subject) Proxy.newProxyInstance(loader, new Class[]{Subject.class}, proxy);
        
        // 用代理對象來執行方法
        proxySubject.request();
    }

可以看到動態代理通過一個代理類來代理N多個被代理類,其實質是對代理者與被代理者進行解耦,是兩者之間沒有直接的耦合關係。

4. 代理模式的應用

在Andorid中,使用代理模式的地方還挺多的

  • Binder本身就是代理模式的實現,Bundle數據從一個進程傳輸到另一個進程時,它會封裝成像 BpBinderBBinder這樣的類來供其他進程來訪問本進程的東西。
    AIDL它使用了Binder,它是一種靜態代理,可以看我之前總結的文章來學習 AIDL是如何體現Binder的:從AIDL來看Binder的通信原理(基於Andorid8.0)
  • Android7.0及之前源碼,Activity與AMS進行通信,使用的就是代理類: ActivityManagerProxy
  • 封裝類本身就是代理模式的靈活應用

5. 小結

首先是代理模式的使用場景

  • 遠程代理
    爲某個對象在不同的內存地址空間提供局部代理。使系統可以將Server部分的實現給隱藏。
    譬如 Binder AIDL的 跨進程通信
  • 虛擬代理
    使用一個代理對象表示一個十分消耗資源的對象並在真正需要時才創建
  • 保護代理
    使用代理控制對原始對象訪問,該類型的代理被用於原始對象有不同訪問權限的情況。
  • 智能引用
    在訪問原始對象時,執行一些自己的附加操作並對指向原始對象的引用計數。
    譬如封裝類。

其次是代理者模式和中介者模式的區別

  • 中介者模式的兩個類是可以互相通過中介者進行訪問的,而代理模式只能用代理類訪問被代理類,而被代理類它是不能夠訪問代理類對象的。
  • 關於代理類和被代理類,靜態代理是1對1的關係,動態代理是1對多的關係,而中介者模式是多對多的關係。

關於靜態代理和動態代理的區別

  • 靜態代理在code階段必須要要知道被代理的對象有什麼功能,通過這個功能來編寫代理類。
  • 動態代理在code階段不需要知道誰是被代理類,只需要寫好通用的代理類,就能在需要的時候去代理任何需要被代理的對象。

兩者的區別就只是在code層上而已。可以按照喜好、具體場景來使用,並無嚴格的優劣之分。

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