對于開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在android開發中,必要的了解一些設計模式又是必須的,因為設計模式在Android源碼中,可以說是無處不在。對于想系統的學習設計模式的同學,這里推薦一本書,《大話設計模式》。
Android常用設計模式系列:
面向對象的基礎特征
面向對象的設計原則
單例模式
模板模式
適配器模式
工廠模式
代理模式
原型模式
策略模式
Build模式
觀察者模式
裝飾者模式
中介模式
門面模式
代理模式
代理模式是非常常見的設計模式之一,寫個筆記,記錄一下我的學習過程和心得。
首先了解一些代理模式的定義。
為其他對象提供一種代理以控制這個對象的訪問。
涉及角色及說明:
Subject(抽象主題類):接口或者抽象類,聲明真實主題與代理的共同接口方法。
RealSubject(真實主題類):也叫做被代理類或被委托類,定義了代理所表示的真實對象,負責具體業務邏輯的執行,客戶端可以通過代理類間接的調用真實主題類的方法。
Proxy(代理類):也叫委托類,持有對真實主題類的引用,在其所實現的接口方法中調用真實主題類中相應的接口方法執行。
Client(客戶端類):使用代理模式的地方。
理解:
- 代理模式屬于結構型模式。
- 代理模式也叫委托模式。
- 生活中,比如代購、打官司等等,實際上都是一種代理模式。
以海外代購為例,在國內的人想買國外的東西只能去找國外的人去進行代購。
1 創建抽象主題類
人都是有購買這個方法的:
public interface People {
void buy();//購買
}
2 創建真實主題類
國內的人想購買某些產品,定義具體的購買過程:
public class Domestic implements People {
@Override
public void buy() {//具體實現
System.out.println("國內要買一個包");
}
}
3 創建代理類
海外的代購黨需要知道是誰(持有真實主題類的引用)想購買啥產品:
public class Oversea implements People {
People mPeople;//持有People類的引用
public Oversea(People people) {
mPeople = people;
}
@Override
public void buy() {
System.out.println("我是海外代購:");
mPeople.buy();//調用了被代理者的buy()方法,
}
}
5 客戶端測試:
public void test() {
People domestic = new Domestic(); //創建國內購買人
People oversea = new Oversea(domestic); //創建海外代購類并將domestic作為構造函數傳遞
oversea.buy(); //調用海外代購的buy()
}
輸出結果:
我是海外代購:
國內要買一個包
靜態代理與動態代理
從代碼的角度來分,代理可以分為兩種:一種是靜態代理,另一種是動態代理。
- 靜態代理就是在程序運行前就已經存在代理類的字節碼文件,代理類和委托類的關系在運行前就確定了。上面的例子實現就是靜態代理。
- 動態代理類的源碼是在程序運行期間根據反射等機制動態的生成,所以不存在代理類的字節碼文件。代理類和委托類的關系是在程序運行時確定。
??下面我們實現動態代理,Java提供了動態的代理接口InvocationHandler,實現該接口需要重寫invoke()方法:
1 創建動態代理類
public class DynamicProxy implements InvocationHandler {//實現InvocationHandler接口
private Object obj;//被代理的對象
public DynamicProxy(Object obj) {
this.obj = obj;
}
//重寫invoke()方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("海外動態代理調用方法: "+method.getName());
Object result = method.invoke(obj, args);//調用被代理的對象的方法
return result;
}
}
2 修改客戶端的測試方法:
public void test() {
People domestic = new Domestic(); //創建國內購買人
DynamicProxy proxy = new DynamicProxy(domestic); //創建動態代理
ClassLoader classLoader = domestic.getClass().getClassLoader(); //獲取ClassLoader
People oversea = (People) Proxy.newProxyInstance(classLoader, new Class[]{People.class}, proxy); //通過 Proxy 創建海外代購實例 ,實際上通過反射來實現的。
oversea.buy();//調用海外代購的buy()
}
輸出結果:
海外動態代理調用方法: buy
國內要買一個包
靜態代理與動態代理比較
靜態代理的缺點:
靜態代理如果接口新增一個方法,除了所有實現類(真實主題類)需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的復雜度。
代理對象只服務于一種類型的對象,如果要服務多類型的對象。必須要為每一種對象都進行代理,靜態代理在程序規模稍大時就無法勝任了。
動態代理的優點:
可以通過一個代理類完成全部的代理功能,接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。當接口方法數量較多時,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。
動態代理的應用使我們的類職責更加單一,復用性更強。
動態代理的缺點:
不能對類進行代理,只能對接口進行代理,如果我們的類沒有實現任何接口,那么就不能使用這種方式進行動態代理(因為$Proxy()這個類集成了Proxy,Java的集成不允許出現多個父類)。
廣泛應用
總結
總結一下代理模式的優缺點
優點
代理作為調用者和真實主題的中間層,降低了模塊間和系統的耦合性。
可以以一個小對象代理一個大對象,達到優化系統提高運行速度的目的。
代理對象能夠控制調用者的訪問權限,起到了保護真實主題的作用。
缺點
由于在調用者和真實主題之間增加了代理對象,因此可能會造成請求的處理速度變慢。
實現代理模式需要額外的工作(有些代理模式的實現非常復雜),從而增加了系統實現的復雜度。
適用場景
當一個對象不能或者不想直接訪問另一個對象時,可以通過一個代理對象來間接訪問。為保證客戶端使用的透明性,委托對象和代理對象要實現同樣的接口。
被訪問的對象不想暴露全部內容時,可以通過代理去掉不想被訪問的內容。
根據適用范圍,代理模式可以分為以下幾種:
- 遠程代理:為一個對象在不同的地址空間提供局部代表,這樣系統可以將Server部分的事項隱藏。
- 虛擬代理:如果要創建一個資源消耗較大的對象,可以先用一個代理對象表示,在真正需要的時候才真正創建。
- 保護代理:用代理對象控制對一個對象的訪問,給不同的用戶提供不同的訪問權限。
- 智能引用:在引用原始對象的時候附加額外操作,并對指向原始對象的引用增加引用計數。