代理模式

介紹

代理模式也稱為委托模式,是一種結(jié)構(gòu)性設(shè)計(jì)模式。

說到代理,可能大部分人都會(huì)有一種陌生又熟悉的感覺,日常生活中好像都能遇到,比如代理上網(wǎng),招商代理,商務(wù)代理等;但又說不出個(gè)具體的一二三來;代理這個(gè)事情如果我們換個(gè)角度,從委托者的角色出發(fā),我們找代理上網(wǎng),是因?yàn)槲覀冊(cè)谠L問某些網(wǎng)站時(shí)存在困難,需要有個(gè)角色來間接的幫我們實(shí)現(xiàn)這個(gè)功能;我們找商務(wù)代理,可能是因?yàn)樵S多事我們不在行或者其他原因,需要找專業(yè)的中間人來幫我們做事。因此,日常生活中我們更多扮演的是委托人的角色,代理以一種中間人的角色,幫我們是處理我們無能為力的事情。

如果從寫代碼的角度出發(fā),當(dāng)我們遇到以下場(chǎng)景:

  • 無法直接訪問某個(gè)對(duì)象
  • 不想直接訪問某個(gè)對(duì)象
  • 訪問某個(gè)對(duì)象存在困難

的時(shí)候,我們就可以通過一個(gè)代理,通過它來間接訪問真正的對(duì)象。

定義及UML圖

定義:

為目標(biāo)對(duì)象提供一種代理,客戶端通過代理去訪問目標(biāo)對(duì)象。

UML 圖

從代理模式的UML 類圖中,我們可以得到如下結(jié)論:

  • 代理對(duì)象和委托對(duì)象需要實(shí)現(xiàn)相同的接口(抽象類)
  • 代理對(duì)象持有委托對(duì)象的引用

可以看到,代理模式非常簡(jiǎn)潔,總共就三個(gè)角色,包括抽象主題,委托者和代理者,下面用代碼簡(jiǎn)單實(shí)現(xiàn)一下基礎(chǔ)的代理模式。

public interface Subject {
    void doSomething();
}

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("This is real doSomeThing");
    }
}

public class ProxySubject implements Subject {

    private Subject mSubject;
    // 代理類持有委托類的引用
    public ProxySubject(Subject realSubject) {
        mSubject = realSubject;
    }

    @Override
    public void doSomething() {
        mSubject.doSomething();
    }
}

public class Client {
    public static void main(String[] args) {
        //創(chuàng)建委托類
        Subject mRealSubject=new RealSubject();
        //創(chuàng)建代理類
        ProxySubject mProxy = new ProxySubject(mRealSubject);
        //由代理類去做具體的操作
        mProxy.doSomething();
    }
}

可以看到RealSubject和ProxySubject都實(shí)現(xiàn)了接口Subject。在客戶端使用ProxySubject的實(shí)例調(diào)用doSomething方法,而不是使用RealSubject的實(shí)例來實(shí)現(xiàn)。

你可能會(huì)好奇,這么做的意義是什么呢?直接用RealSubject的實(shí)例來調(diào)用doSomething方法不也可以嗎?何必多此一舉。試想,如果現(xiàn)在有很多個(gè)委托類,他們各自的實(shí)現(xiàn)都不同,客戶端只關(guān)心doSomething 的調(diào)用,而不關(guān)心具體的實(shí)現(xiàn),這樣代理類就可以在其內(nèi)部屏蔽委托類之間的差異了,這也是客戶端不想關(guān)注的事情。這么說可能有點(diǎn)暈,下面就通過Android源碼中的實(shí)現(xiàn)來感受一下。

Android 中的代理模式

平時(shí)寫代碼的時(shí)候,可能感覺代理模式?jīng)]怎么遇到過。其實(shí)不然,甚至可以說代理模式是我們最常用到的一種設(shè)計(jì)模式。這里就來看看幾乎天天都在使用的AppCompatActivity。

最早的時(shí)候,我們創(chuàng)建自己的Activity都是直接繼承android.app.Activity。后來隨著Android版本的升級(jí),我們創(chuàng)建的Activity會(huì)繼承AppCompatActivity。這里的Compat其實(shí)就是Compatible(兼容)的縮寫,那么他是怎么實(shí)現(xiàn)兼容的呢。

onCreate

onCreate()方法是整個(gè)Activity生命周期的開始。AppCompatActivity又是怎么實(shí)現(xiàn)他的呢。

AppCompatActivity-onCreate()

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        final AppCompatDelegate delegate = getDelegate();
        delegate.installViewFactory();
        delegate.onCreate(savedInstanceState);
        ……
    }

可以看到這里他并沒具體去實(shí)現(xiàn)onCreate,而是使用一個(gè)AppCompatDelegate實(shí)例的onCreate()方法去實(shí)現(xiàn)。繼續(xù)看getDelegate 的實(shí)現(xiàn)。

AppCompatActivity-getDelegate()

    @NonNull
    public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

可以看到這個(gè)實(shí)例創(chuàng)建是在AppCompatDelegate類中。接著看create的實(shí)現(xiàn)

AppCompatDelegate-create()

    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);
        }
    }

可以看到,這里就不同的Android版本,分別返回了不同的AppCompatDelegate。如果去看源碼(這里的源碼分析不是重點(diǎn),就不貼出了,直接給出結(jié)論),我們會(huì)發(fā)現(xiàn),從
AppCompatDelegateImplN到AppCompatDelegateImplV9,是子類到父類的關(guān)系,之間是依次繼承。而AppCompatDelegateImplV9又繼承自AppCompatDelegateImplBase(抽象類),而這個(gè)AppCompatDelegateImplBase則是繼承自AppCompatDelegate。

到這里,結(jié)合一開始我們所說的代理模式的內(nèi)容,我們很容易總結(jié)出以下結(jié)論:

  • AppCompatDelegate 同時(shí)兼顧了抽象主題和代理類的角色
  • AppCompatDelegateImplN,AppCompatDelegateImplV23等這些都是委托類,他們都繼承自AppCompatDelegate方法。

通過AppCompatDelegate.java(點(diǎn)擊可直接查看)的源碼,我們可以發(fā)現(xiàn),這個(gè)抽象類內(nèi)部定義了一系列和 Activity 相關(guān)的抽象方法,包括Activity生命周期函數(shù),setContentView,setSupportActionBar等。我們知道,子類通過繼承父類,可以擴(kuò)展(spuer)或直接覆蓋父類的方法實(shí)現(xiàn)。 AppCompatDelegateImplV9 這個(gè)類是AppCompatDelegate的具體實(shí)現(xiàn),之后的版本,就可以通過繼承AppCompatDelegateImplV9來擴(kuò)展或修改一些方法實(shí)現(xiàn),通過AppCompatDelegate 在create方法中創(chuàng)建不同的委托類來完成不同的實(shí)現(xiàn),而我們?cè)葘懞玫拇a也不會(huì)被破壞,可以看到Android源碼對(duì)Activity兼容這個(gè)事做的非常巧妙。AppCompatDelegate主要是對(duì)ActionBar的兼容及夜間模式的處理做了一些方便開發(fā)者實(shí)現(xiàn)的處理;這里就不再具體分析了。

當(dāng)然,代理模式這個(gè)幾乎找不到缺點(diǎn)的設(shè)計(jì)模式,在Android源碼中的應(yīng)用也是比較廣泛,基本上關(guān)于兼容性的實(shí)現(xiàn),都會(huì)用到以上思路,比如NotificationCompatImpl幾乎使用了和AppCompatDelegate同樣的思路,實(shí)現(xiàn)了在手機(jī)通知欄中實(shí)現(xiàn)不同的通知樣式。除了兼容性的處理,另外一個(gè)比較經(jīng)典的實(shí)現(xiàn)就是Binder了,作為跨進(jìn)程通信的核心理念,Binder巧妙的使用了代理模式,處理了我們無法在不同應(yīng)用之間共享和傳遞數(shù)據(jù)的問題。關(guān)于Binder的分析,網(wǎng)上真的太多了,這里就不再贅述了,有興趣的同學(xué)可以看看這篇代理模式在Binder中的使用.

動(dòng)態(tài)代理

以上的分析中,委托類是由我們直接創(chuàng)建好的;現(xiàn)實(shí)中可能還有這樣一種場(chǎng)景,委托類并不是在程序編譯的時(shí)候創(chuàng)建,而是在運(yùn)行的過程中通過Java的反射機(jī)制動(dòng)態(tài)的進(jìn)行創(chuàng)建,這樣的代理模式成為動(dòng)態(tài)代理,對(duì)應(yīng)的之前我們所說的就是靜態(tài)代理了。

其實(shí),動(dòng)態(tài)代理的實(shí)現(xiàn)沒有什么可說的,說白了都是模板代碼,Java為開發(fā)者提供了InvocationHandler,實(shí)現(xiàn)該接口重寫其invoke 方法即可。

還是以之前的Subject為例

public interface Subject {
    void doSomething();
}

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("This is real doSomeThing");
    }
}


public class DynamicProxyHandler implements InvocationHandler {
    private Object mObject;


    public DynamicProxyHandler(Object object) {
        mObject = object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        return method.invoke(mObject, objects);
    }
}


public class MainClass {
    public static void main(String[] args) {
        // 委托類
        Subject mRealSubject = new RealSubject();
        // 委托類classLoader
        ClassLoader mClassLoader = mRealSubject.getClass().getClassLoader();
        // 委托類對(duì)應(yīng)的ProxyHandler
        DynamicProxyHandler mProxyHandler = new DynamicProxyHandler(mRealSubject);
        Class[] mClasses = new Class[]{Subject.class};
        // 代理類
        Subject proxySubject = (Subject) Proxy.newProxyInstance(mClassLoader, mClasses, mProxyHandler);
        // 代理類調(diào)用方法
        proxySubject.doSomething();
        
    }
}

這里可以看到,DynamicProxyHandler內(nèi)部持有的并不是一個(gè)具體的對(duì)象,而是Object類,而在其invoke方法中,又會(huì)根據(jù)具體的Object對(duì)象及參數(shù)調(diào)用其對(duì)應(yīng)的方法。這樣當(dāng)我們?cè)诳蛻舳苏{(diào)用時(shí),完全是根據(jù)委托類通過Proxy.newProxyInstance方法動(dòng)態(tài)的創(chuàng)建代理類。在上面的代碼中,我們是通過委托類RealSubject動(dòng)態(tài)的創(chuàng)建了一個(gè)代理類,通過代理類調(diào)用抽象主題中定義好的方法,實(shí)際上就會(huì)調(diào)用委托類中的具體實(shí)現(xiàn)。而在Java中,我們可以通過反射機(jī)制,動(dòng)態(tài)的創(chuàng)建類及其實(shí)例,因此,我們便可以在運(yùn)行時(shí)通過不同的委托類,更靈活的創(chuàng)建代理類,從而實(shí)現(xiàn)不同的功能。

關(guān)于動(dòng)態(tài)代理,這篇十分鐘理解Java之動(dòng)態(tài)代理分析的非常好,有興趣的同學(xué)可以再看看。

在Android中,關(guān)于動(dòng)態(tài)代理的使用,最經(jīng)典的莫過于這幾年最火熱的Retrofit了。這里可以簡(jiǎn)單看一下。


public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

上面的實(shí)現(xiàn),現(xiàn)在大家應(yīng)該很熟悉了,當(dāng)我們用Retrofit實(shí)例,調(diào)用其create方法時(shí),到底發(fā)生了什么呢?

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

可以看到,這里就是一個(gè)典型的動(dòng)態(tài)代理實(shí)現(xiàn),通過serviceMethod.callAdapter.adapt返回了一個(gè)service對(duì)象的代理對(duì)象,在上面的例子里,就是返回了一個(gè)GitHubService的代理對(duì)象,這樣我們就可以通過這樣一個(gè)對(duì)象去調(diào)用GitHubService中定義好的各種網(wǎng)絡(luò)請(qǐng)求,而不用在使用的時(shí)候再去決定是POST請(qǐng)求還是GET請(qǐng)求,參數(shù)是放在Body里還是params里,因?yàn)镽etrofit 通過把反射注解和動(dòng)態(tài)代理的巧妙結(jié)合,屏蔽了復(fù)雜的參數(shù)拼接操作,把所有我們需要對(duì)OKHttp的進(jìn)行傳遞的參數(shù),動(dòng)態(tài)的幫我們傳遞了,一旦在接口中定義好了使用方式,就可以非常方便的獲取到okhttp中最關(guān)鍵的Call了,有了Call我們就可以通過execute或者是enqueue發(fā)起網(wǎng)絡(luò)請(qǐng)求了。

總結(jié)

以上就是對(duì)代理模式的分析,總的來說代理模式的結(jié)構(gòu)非常簡(jiǎn)單;包括抽象主題,委托類,代理類三個(gè)核心角色,從大的方向上可以分為靜態(tài)代理和動(dòng)態(tài)代理兩大類;通過靜態(tài)代理的方式,在開發(fā)迭代的過程中,為實(shí)現(xiàn)兼容性提供了一種非常友好的實(shí)現(xiàn)思路;在日常開發(fā)中,如果我們使用的對(duì)象之間有著強(qiáng)烈的耦合,可是思考一下是否可以通過代理模式解耦;同時(shí),當(dāng)我們需要擴(kuò)展某個(gè)類的部分功能時(shí),但又不想去破壞原有的功能或者是根本無法修改時(shí),我們可以考慮代理模式,但也要明白,通過代理模式我們能做的也只能是功能擴(kuò)展,想要更新委托類中已經(jīng)實(shí)現(xiàn)的內(nèi)容他是無能為力的。

動(dòng)態(tài)代理,可以根據(jù)運(yùn)行時(shí)的委托類動(dòng)態(tài)的生成代理類,這樣就減輕了代理類的負(fù)擔(dān),避免在編碼階段就具體的委托類再做各種判斷了。

代理模式很簡(jiǎn)單,也很實(shí)用,但不要忘記代理類委托類需要實(shí)現(xiàn)功能的接口或抽象類,不要忽略了這一點(diǎn)。

好了,關(guān)于代理模式的分析就到這里了


最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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