前言
今天分享的文章是關(guān)于設(shè)計模式中的代理模式,代理模式在Android中的應(yīng)用還是很廣的放祟,而且也是比較常用的一種設(shè)計模式恶迈。
本文主要是寫個簡單的demo來帶著大家熟悉認識代理模式光戈,最后舉例分析一個我們Android常用的網(wǎng)絡(luò)框架Retrofit中代理模式的使用杂抽,加深對代理模式的設(shè)計。
設(shè)計模式之代理模式
代理模式的定義
什么是代理模式生音?
- 簡單一句話解釋:為其他對象提供代理對象宁否,以控制對這個對象的訪問
- 詳細的解釋:為其他對象提供代理以控制對這個對象的訪問,代理對象起到了中介作用缀遍,不涉及功能服務(wù)慕匠,亦可增加額外的服務(wù);
代理模式的分類:
- 遠程代理:為不同的地理對象提供局域網(wǎng)代表對象域醇。典型的設(shè)計有:C/S架構(gòu)屬于遠程代理的縮影
- 虛擬代理:根據(jù)需要將資源消耗很大的對象進行延遲台谊,真正需要的時候再創(chuàng)建。典型設(shè)計:經(jīng)常我們看到很多APP在加載圖片的時候譬挚,會先加載一個默認的圖片锅铅,等真正的圖片加載完了之后再顯示出來,這樣非常的友好减宣。
- 智能引用代理:提供對目標對象的額外的服務(wù)盐须。典型設(shè)計:現(xiàn)實場景中隨處可見,我們的火車蚪腋、汽車票代售處、代購等等都是屬于代理模式的范疇姨蟋。
- 保護代理:控制用戶的訪問權(quán)限屉凯。典型設(shè)計:就像我們的公眾號的文章留言功能,只有你這個用戶關(guān)注了該公眾號之后才能留言眼溶,否則你就只能瀏覽不能留言悠砚。
代理模式的實現(xiàn)
Android 中比較常用的代理模式——智能引用代理,我們就以此為例堂飞,一步一步的來實現(xiàn)代理模式灌旧。
代理的實現(xiàn)方式主要有兩種:
靜態(tài)代理:
代理和被代理對象在代理之前都是被確定的,他們都實現(xiàn)相同的接口或者是繼承相同的抽象類绰筛,這里一張經(jīng)典的圖
上面這張圖我們可以看出:ProxySubject和RealSbuject都是繼承了Subject枢泰,Client請求Subjct的doSomething()方法,本應(yīng)該是調(diào)用RealSubject中的doSomething()方法铝噩,但是我們實際看到的卻是調(diào)用的ProxySubject方法中的doSomething()方法衡蚂,在ProxySubject方法中的doSomething()方法中再去調(diào)用RealSubject中的doSomething()方法。我們實際看到的是ProxySubject方法中的doSomething()方法,這樣就完成了代理毛甲。
靜態(tài)代理:
靜態(tài)代理之聚合的方式
1.按照之前的定義我們先定義一個接口:
public interface Subject{
public void doSomeThing();
}
2.創(chuàng)建被代理的角色年叮,邏輯執(zhí)行者:
public class RealSubject implements Subject{
@Override
public void doSomeThing() {
//do some things
}
}
3.創(chuàng)建我們的代理類:
public class Proxy implements Subject{
private Subject subject = null;
public Proxy(){
subject = new RealSubject();
}
@Override
public void doSomeThing() {
subject.doSomeThing();
}
}
可以看到我們在構(gòu)造方法里面創(chuàng)建了一個RealSubjecj的實現(xiàn)類對象,在調(diào)用Proxy的doSomething()方法的時候玻募,實際是調(diào)用的RealSubject對象的的doSomething()方法只损,但是經(jīng)常我們還會附加一些邏輯在里面比如我們需要打印一些Log日志,直接這樣加就可以了:
public class Proxy implements Subject{
private Subject subject = null;
public Proxy(){
subject = new RealSubject();
}
@Override
public void doSomeThing() {
log.d("TAG","代理開始...")
subject.doSomeThing();
log.d("TAG","代理開結(jié)束...")
}
}
這樣的好處就是正在實現(xiàn)類的邏輯不用做任何改變即可實現(xiàn)七咧。
寫到這里可能有很多的人有疑問跃惫,我直接創(chuàng)建RealSubject的實現(xiàn)類,調(diào)用doSomething()方法和你在代理中再創(chuàng)建調(diào)用有啥區(qū)別坑雅,反而增加了類和代碼辈挂。按照這個說法確實是如此,但是不要忘記了我們Android 的設(shè)計模式中是有六大原則:
- 單一職責原則
- 里氏替換原則
- 依賴倒置原則
- 接口隔離原則
- 迪米特原則
- 開閉原則
這幾個原則不是我們的這篇文章重點要講述的裹粤,感興趣的朋友可自行查
因此如果直接用RealSubject調(diào)用doSomething()终蒂,就不符合我們的設(shè)計模式的六大原則。
靜態(tài)代理之繼承的方式
和上面的聚合方式只有最后的代理類有點區(qū)別:
public class Proxy extends Subject{
@Override
public void doSomeThing() {
log.d("TAG","代理開始...")
super.doSomeThing();
log.d("TAG","代理開結(jié)束...")
}
}
這種方式和上訴的方式有著相同結(jié)果遥诉,也稱為靜態(tài)代理之繼承方式拇泣。
可能大家都可能會想到,既然靜態(tài)代理有兩種模式矮锈,那我們使用的時候到底應(yīng)該使用哪種呢霉翔?
其實這個問題我們先不說其他的,就以實現(xiàn)接口和繼承來說苞笨,必然是實現(xiàn)接口更加符合我們的設(shè)計模式债朵。接下來我舉個例子我想大家就應(yīng)該很清楚使用哪種方式更加的合適:
就以上面的例子來說,上面的繼承模式中我只添加了Log的功能瀑凝,假設(shè)我要為我們的代理增加兩個功能:1.計算時間
2.增加權(quán)限管理⌒蚵現(xiàn)在我們的代理類就又三個功能:1.Log能力 2.時間 3.權(quán)限。假設(shè)需求不斷的改變:1/2/3能力的順序不斷改變粤咪,我們是不是就要不斷的增加繼承的類谚中,所以這種方式并不是我們推薦的方式,也不符合我們的設(shè)計模式寥枝。因此宪塔,我們的聚合的方式是更加的適合靜態(tài)代理模式。
因此綜上所述囊拜,我們在使用靜態(tài)代理模式的時候某筐,我們更加的傾向于使用聚合的方式,也就是實現(xiàn)接口這種方式冠跷,而非繼承来吩。
可能有的小伙伴就會說了敢辩,聚合的方式到底該怎么實現(xiàn)呢?
創(chuàng)建接口
public interface ISubject {
//出行
public void doSomething();
}
實現(xiàn)接口真實執(zhí)行邏輯
public class RealSubject implements ISubject {
private static final String TAG = "RealSubject";
@Override
public void doSomething() {
Log.e(TAG, "doSomething: 執(zhí)行中");
}
}
創(chuàng)建時間代理
public class TimeProxy implements ISubject {
private static final String TAG = "TimeProxy";
private ISubject iSubject;
public TimeProxy(ISubject iSubject) {
this.iSubject = iSubject;
}
@Override
public void doSomething() {
Log.e(TAG, "開始執(zhí)行時間...");
iSubject.doSomething();
Log.e(TAG, "結(jié)束執(zhí)行時間...");
}
建log代理
public class LogProxy implements ISubject {
private static final String TAG = "LogProxy";
private ISubject iSubject;
public LogProxy(ISubject iSubject) {
this.iSubject = iSubject;
}
@Override
public void doSomething() {
Log.e(TAG, "日志開始弟疆。戚长。。");
iSubject.doSomething();
Log.e(TAG, "日志結(jié)束怠苔。同廉。。");
}
}
我們在MainActivity中執(zhí)行兩種方式:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RealSubject realSubject = new RealSubject();
//先記錄時間 在打印日志
ISubject logProxy = new LogProxy(realSubject);
TimeProxy timeProxy = new TimeProxy(logProxy);
timeProxy.doSomething();
Log.e(TAG, "============================================ ");
//先打印日志 再記錄時間
TimeProxy timeProxy1 = new TimeProxy(realSubject);
ISubject logProxy1 = new LogProxy(timeProxy1);
logProxy1.doSomething();
}
運行結(jié)果:
07-09 23:20:06.516 27723-27723/com.lt.proxy E/TimeProxy: 開始執(zhí)行時間...
07-09 23:20:06.516 27723-27723/com.lt.proxy E/LogProxy: 日志開始柑司。迫肖。。
07-09 23:20:06.516 27723-27723/com.lt.proxy E/RealSubject: doSomething: 執(zhí)行中
07-09 23:20:06.516 27723-27723/com.lt.proxy E/LogProxy: 日志結(jié)束攒驰。蟆湖。。
07-09 23:20:06.516 27723-27723/com.lt.proxy E/TimeProxy: 結(jié)束執(zhí)行時間...
07-09 23:20:06.516 27723-27723/com.lt.proxy E/MainActivity: ============================================
07-09 23:20:06.516 27723-27723/com.lt.proxy E/LogProxy: 日志開始玻粪。隅津。。
07-09 23:20:06.516 27723-27723/com.lt.proxy E/TimeProxy: 開始執(zhí)行時間...
07-09 23:20:06.516 27723-27723/com.lt.proxy E/RealSubject: doSomething: 執(zhí)行中
07-09 23:20:06.516 27723-27723/com.lt.proxy E/TimeProxy: 結(jié)束執(zhí)行時間...
07-09 23:20:06.516 27723-27723/com.lt.proxy E/LogProxy: 日志結(jié)束劲室。伦仍。。
復制代碼這樣我們就可以看出我們的聚合方式的靜態(tài)代理的優(yōu)點很洋。
到了這里我想很多的小伙伴都看到了一個缺點充蓝,要是我們的真實執(zhí)行邏輯的類RealSubject有很多種類型呢?例如我們今天要去上班喉磁,你會有多種方式:走路谓苟、騎自行車、開汽車协怒、坐地鐵涝焙、公交等等。斤讥。纱皆。很多的方式湾趾,難道我們每一個都要去實現(xiàn)一篇芭商,然后添加日志功能、時間功能搀缠?這樣是不是太不符合我們的代碼的設(shè)計模式了铛楣。因此,我們有沒有一種動態(tài)的方式艺普,傳入什么出行的工具簸州,那么就給我記錄什么工具呢鉴竭?那就是我們接下來要講解的動態(tài)代理模式,也是我們最重要的一種模式岸浑。
動態(tài)代理模式
學習動態(tài)代理之前搏存,首先我們要知道InvocationHandler類:
/**
* Created by Scorpio on 2018/7/9.
* 該類經(jīng)常稱之為事務(wù)處理類
*/
public class DynamicProxy implements InvocationHandler {
/**
*
* @param proxy 代理類
* @param method 被代理的方法
* @param args 被代理方法的參數(shù)
* @return 返回代理對象
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
InvocationHandler是個接口,只有一個抽象方法 invoke(Object proxy, Method method, Object[] args)矢洲,四個參數(shù)的意義:
- param proxy 代理類
- param method 被代理的方法
- param args 被代理方法的參數(shù)
- return 方法的返回值
其次就是我們的Proxy即動態(tài)代理類璧眠,它當中有個方法:Proxy.newProxyInstance(ClassLoader loader,Class class,Proxy proxy)
public class RealSubject implements ISubject {
private static final String TAG = "RealSubject";
@Override
public void doSomething() {
Log.e(TAG, "doSomething: 執(zhí)行中");
}
}
public class RealSubject2 implements ISubject {
private static final String TAG = RealSubject2.class.getName();
@Override
public void doSomething() {
Log.e(TAG, "doSomething: 執(zhí)行中");
}
}
2.創(chuàng)建事務(wù)處理類
/**
* Created by Scorpio on 2018/7/9.
* 該類經(jīng)常稱之為事務(wù)處理類
*/
public class DynamicProxy implements InvocationHandler {
private static final String TAG = "DynamicProxy";
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
/**
* @param proxy 代理類
* @param method 被代理的方法
* @param args 被代理方法的參數(shù)
* @return 返回代理對象
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e(target.getClass().getName(), "開始日志。读虏。责静。");
method.invoke(target);
Log.e(target.getClass().getName(), "結(jié)束日志。盖桥。灾螃。");
return null;
}
}
3.代碼中使用:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "===================RealSubject========================= ");
RealSubject realSubject = new RealSubject();
DynamicProxy dynamicProxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
ISubject proxyInstance = (ISubject) Proxy.newProxyInstance(classLoader, realSubject.getClass().getInterfaces(), dynamicProxy);
proxyInstance.doSomething();
Log.e(TAG, "===================RealSubject2========================= ");
RealSubject2 realSubject2 = new RealSubject2();
DynamicProxy dynamicProxy2 = new DynamicProxy(realSubject2);
ClassLoader classLoader2 = realSubject2.getClass().getClassLoader();
ISubject proxyInstance2 = (ISubject) Proxy.newProxyInstance(classLoader2, realSubject2.getClass().getInterfaces(), dynamicProxy2);
proxyInstance2.doSomething();
}
}
運行結(jié)果:
07-10 00:07:30.284 30241-30241/com.lt.proxy E/MainActivity: ===================RealSubject=========================
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 開始日志。揩徊。腰鬼。
07-10 00:07:30.285 30241-30241/com.lt.proxy E/RealSubject: doSomething: 執(zhí)行中
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject: 結(jié)束日志。靴拱。垃喊。
07-10 00:07:30.285 30241-30241/com.lt.proxy E/MainActivity: ===================RealSubject2=========================
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject2: 開始日志。袜炕。本谜。
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject2: doSomething: 執(zhí)行中
07-10 00:07:30.285 30241-30241/com.lt.proxy E/com.lt.proxy.RealSubject2: 結(jié)束日志。偎窘。乌助。
復制代碼這樣我們的動態(tài)代理就實現(xiàn)成功了,我們不管傳入什么樣的實現(xiàn)類陌知,都可以通過動態(tài)代理獲取到代理對象他托,進而實現(xiàn)代理功能。
Android Retrofit 中的動態(tài)代理模式應(yīng)用
通過我上面對Retrofit源碼的截圖仆葡,以及上面的標注就可以看出赏参,這個Retrofit的creat()方法是個典型的動態(tài)代理的模式
總結(jié)
優(yōu)點:
1、代理模式能夠協(xié)調(diào)調(diào)用者和被調(diào)用者沿盅,在一定程度上降低了系統(tǒng)的耦合度把篓;
2、代理對象可以在客戶端和目標對象之間起到中介的作用腰涧,這樣起到了保護目標對象的作用韧掩。
缺點:
1、由于在客戶端和真實對象之間增加了代理對象窖铡,因此有些類型的代理模式可能會造成請求的處理速度變慢疗锐;
2坊谁、實現(xiàn)代理模式需要額外的工作,有些代理模式的實現(xiàn)非常復雜滑臊。
————————————————
版權(quán)聲明:本文為CSDN博主「lt_sc」的原創(chuàng)文章口芍,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明雇卷。
原文鏈接:https://blog.csdn.net/qq_34560959/article/details/80981890