
介紹
代理模式也稱為委托模式,是一種結(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)于代理模式的分析就到這里了