設計模式解析之代理模式
本文關鍵詞:設計模式解析,由筆耕文化傳播整理發(fā)布。
設計模式解析之代理模式
設計模式-代理模式代理模式的概念 代理模式(proxy pattern)是一種結構型的設計模式,代理模式在程序開發(fā)中的運用非常廣泛。簡單地描述代理模式就是:為其他對象(被代理對象)提供一種代理以控制對原有對象的操作。實際的行為是由被代理對象完成的。
代理模式可以分為兩部分,靜態(tài)代理 和 動態(tài)代理,它們的區(qū)別將在下面詳細介紹。
角色介紹: Suject: 抽象主題類
該類的主要職責是申明真是主題與代理的共同接口方法,該類既可以是個抽象類也可以是個接口(具有抽象方法)。
RealSubject: 真實主題類
該類也稱為委托類或者被代理類,改類定義了代理所表示的真是對象(也就是實現(xiàn)了抽象方法),由其執(zhí)行具體的業(yè)務邏輯。
ProxySubject:代理類
這個類的對象持有一個對真實主題的引用,在這個類所實現(xiàn)的接口方法中調(diào)用真實主題類中相應的方法執(zhí)行,這樣就實現(xiàn)了代理的目的。
Client:客戶類
也就是使用代理類的類型,客戶類通過代理類間接地調(diào)用了真實主題類中定義的方法。
代理模式的實現(xiàn)簡單的例子針對,上方的角色介紹,舉一個簡單的例子:在現(xiàn)實的世界中,打公司一般有,原告 和原告的代理律師這樣兩個角色,他們要做的事情是 辯護。于是,我們可以實現(xiàn)以下幾個類。
抽象主題類:interface IDefender,這個接口中有個 抽象方法 abstract public void defend(); 表示辯護這個行為。
[code]public interface IDefender {
abstract public void defend();//辯護行為
}
真實主題類: class Accuser,實現(xiàn)IDefender這個接口,具有具體的邏輯行為
[code] public class Accuser implements IDefender {
@Override
public void defend() {
System.out.println("被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩");
}
}
代理類:AccuserProxy 類,原告請了個代理律師幫助它打官司,也就是AccuserProxy,由代理律師控制原告的表現(xiàn)
[code]public class AccuserProxy implements IDefender {
//持有被代理類的引用
private IDefender iDefend;
public AccuserProxy(IDefender iDefend) {
this.iDefend = iDefend;
}
@Override
public void defend() {
beforeDefend();
// 被代理類的行為
iDefend.defend();
afterDefend();
}
//修飾的方法
private void beforeDefend(){
System.out.print("我是原告律師,以下是我方辯詞");
}
//修飾的方法
private void afterDefend(){
System.out.print("辯護完畢");
}
}
客戶端:Client類,使用代理類的角色
[code]public class Client {
public static void main(String[] args) {
Accuser accuser = new Accuser();
accuser.defend();//此時輸出 被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩
AccuserProxy accuserProxy = new AccuserProxy(accuser);
accuserProxy.defend();
// 輸出:我是原告律師,以下是我方辯詞,被告嚴重侵犯了公民的人身自由權...嗶哩嗶哩,辯護完畢
/////////////////////////////////////////////////////
// 因為,我們的代理類和被代理類 實現(xiàn)的是同一接口,如果將引用類型 寫成IDfender,
// 那么在調(diào)用 defend(),方法的使用 客戶完全感覺不到被代理類的存在,當然因為我們這里的
// 被代理類是通過構造函數(shù)傳進去的,軟件開發(fā)中,有時候直接在被代理類中實例化代理類,這樣使用起來就更完美了。
IDefender accuser2 = new Accuser();
IDefender accuser2Proxy = new AccuserProxy(accuser2);
accuser2.defend();
}
}
代理模式的運用符合開閉原則的定義,軟件中的對象(類、模塊、函數(shù))應該對于拓展是開發(fā)的,對于修改是封閉的。我們不需要修改被代理類 (Accuser),使用代理模式就可以實現(xiàn)對原有功能的加強。以上代碼只是一個簡答的運用場景,現(xiàn)實開發(fā)中代理模式的運用非常廣泛,它可以解決多方面的問題。
Androd 開發(fā)中的運用例子 Android API的版本迭代很快,在每次的版本更新中 通常會加強一些原有的功能,對原有的類會增加新的接口,而每個版本能夠調(diào)用的API 可能會都不同,比如 狀態(tài)欄 Notification 。
Notfification可以分為4類,一類是正常視圖,也就是我們通常在狀態(tài)欄看到的 高度為 64dp 的長條狀通知視圖;一類是在 API16 中引入的以 Style 方式展示的 MediaStyle、InboxStyle、BigTextStyle 和 BigPictureStyle 四種Notification 風格樣式;一類也是在 API16 中引入的可以將通知視圖顯示為256dp 高度大視圖的 bingContentView;最后一類是在 L 中引入的 headsUpContentVIew
現(xiàn)在,我們的 App需要根據(jù)不同的版本實例化不同 Notification 顯示不同的視圖,在高版本顯示的視圖當然就更豐富,低版本更單調(diào)。如果你直接在 Activity 中判斷版本,加上一坨的 switch 或者 if else 語句當然也是可以的。但是,我們現(xiàn)在來看一下 運用設計模式的思想,如何將代碼抽離,對于客戶端來說(Activity or Fragment),不應該關心這些細節(jié)。
以下的代碼來自《Android源碼設計模式解析與實戰(zhàn)》一書。
抽象主題類: Notify類, Notify抽象類 聲明了 NotificationManager 和NotificatoinCompat.Builder 2個成員變量來處理和通知的一些邏輯。在構造函數(shù)中初始化所有子類都會調(diào)用的邏輯方法。
[code]public abstract class Notify {
protected Context context;
protected NotificationManager nm;
protected NotificationCompat.Builder builder;
public Notify(Context context){
this.context = context;
nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(PendingIntent.getActivities(
context,
0,
new Intent[]{new Intent(context, NotifyActivity.class)},
PendingIntent.FLAG_UPDATE_CURRENT));
}
/**
* 發(fā)送一條通知
*/
public abstract void send();
/**
* 取消一條通知
*/
public abstract void cancel();
}
真實主題類:NotifyNormal ,繼承抽象主題類,簡單重寫抽象方法
[code]public class NotifyNormal extends Notify{
public NotifyNormal(Context context) {
super(context);
}
@Override
public void send() {
Notification n = builder.build();
n.contentView = new RemoteViews(context.getPackageName(),
R.layout.remote_notify_proxy_normal);
nm.notify(0,n);
}
@Override
public void cancel() {
nm.cancel(0);
}
}
真實主題類 : 與NormalNotify的不同在于 還實現(xiàn)了bigContentView 的初始化,,這是在 API 16以上才能調(diào)用的
[code]public class NotifyBig extends Notify{
public NotifyBig(Context context) {
super(context);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void send() {
Notification n = builder.build();
n.contentView = new RemoteViews(context.getPackageName(), R.layout.remote_notify_proxy_normal);
n.bigContentView = new RemoteViews(context.getPackageName(),R.layout.remote_notify_proxy_big);
nm.notify(0,n);
}
@Override
public void cancel() {
nm.cancel(0);
}
}
源碼中還有個 NotifyHeadUp 類,它的唯一區(qū)別就是 在bigContentVie 的基礎上還能實現(xiàn) Notification 的 headsUpContentView ,這個 View 是在 API 20以上 才能使用的,當我們的 App 以全屏的方式展開的時候如果收到了通知,這個 View 就會浮動展示于屏幕頂部。
代理類: NotifyProxy ,持有被代理類對象,在這個示例中,我們根據(jù)不同的運行時環(huán)境 API 實例化不同的 被代理類對象。
[code]public class NotifyProxy extends Notify{
//代理類持有被代理類的引用
private Notify notify;
public NotifyProxy(Context context) {
super(context);
//根據(jù)版本實例化不同的被代理對象
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
notify = new NotifyHeadUp(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
notify = new NotifyBig(context);
} else {
notify = new NotifyNormal(context);
}
}
@Override
public void send() {
notify.send();
}
@Override
public void cancel() {
notify.cancel();
}
}
客戶端類:也就是我們的Activity,在 Activity 中,我們直接將 Context 傳入,代理類會幫我們實例化合適的被代理對象。
[code]new NotifyProxy(NotifyActivity.this).send();
在這個實例中,我們通過代理模式 ,使用一個代理類來針對不同的運行時系統(tǒng)版本,實例化不同的 Notificaition 的子類,而在客戶端中 簡單地調(diào)用代理類的send方法就可以。
靜態(tài)代理模式總結 代理模式通過代理類對外部提供統(tǒng)一的接口,在代理類中實現(xiàn)對被代理類的附加操作,從而可以在不影響外部調(diào)用的情況下實現(xiàn)系統(tǒng)的拓展,我覺得代理模式可能在一個程序項目的開發(fā)初期運用不到,而在項目成型而又有了新的變化、升級等,可以考慮用代理模式來實現(xiàn),這樣可以不需要修改原有的代碼。
動態(tài)代理模式 其實上文所講述的內(nèi)容只是代理模式的一部分,代理模式還有更為強大的動態(tài)代理模式。以下是這 2 個的區(qū)別:
靜態(tài)代理模式:在我們的代碼運行前,代理類的class編譯文件就已經(jīng)存在了
動態(tài)代理模式:在 code 階段并不存在被代理類,而且并不知道要代理哪個對象,利用 Java 的反射機制在運行期動態(tài)地生成代理者的對象,代理誰將會在代碼執(zhí)行階段決定。
動態(tài)代理模式的實現(xiàn) Java 已經(jīng)為我們提供了一個便捷的動態(tài)代理接口 InvocationHandler ,我們重寫其調(diào)用方法 invoke。以前面 我們的代理律師的例子為例,看看具體的操作是怎么樣的
[code]public class DynamicProxy implements InvocationHandler{
private Object object;// 被代理類的類引用
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeDefend();
//調(diào)用被代理類對象的方法
Object result = method.invoke(object, args);
afterDefend();
return result;
}
private void beforeDefend(){
System.out.print("我是原告律師,以下是我方辯詞");
}
private void afterDefend(){
System.out.print("辯護完畢");
}
}
解釋一下這個 invoke 方法,我們通過 invoke 方法來調(diào)用具體的被代理方法,也就是真實的方法,如果對反射機制了解的話, method.invoke(object,args) 這句代碼應該很好理解,object 是被代理類,method 是被代理類的方法,args 是方法的參數(shù)。
我們僅僅是實現(xiàn)了 InvocationHandler 的接口,那么接下來該做什么呢?怎么實現(xiàn)動態(tài)生成代理者對象呢?Java 的 java.lang.reflect 包下 還有一個 Proxy 類,它有個靜態(tài)方法 newProxyInstance(),我們使用這個方法生成。以前面 我們的代理律師的例子為例,
[code]public class Client {
public static void main(String[] args) {
//被代理類
IDefender accuser = new Accuser();
accuser.defend();
DynamicProxy proxy = new DynamicProxy(accuser);
ClassLoader loader = accuser.getClass().getClassLoader();
IDefender proxyIDefender = (IDefender) Proxy.newProxyInstance(loader, new Class[]{IDefender.class}, proxy);
proxyIDefender.defend();
}
}
這個客戶端的輸出結果,和之前是一樣的,可以發(fā)現(xiàn),我們將之前代理類的工作,轉(zhuǎn)換到 InvocationHandler 的 invoke() 方法去執(zhí)行,不再需要關心到底需要代理誰。
動態(tài)代理在 Retrofit 框架中的運用 Retrofit 是 Android 上流行的 Http Client請求庫先看以下一段代碼
[code]interface GitHubService {
@GET("/repos/{owner}/{repo}/contributors")
List<Contributor> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
[code]Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.build();
//代理模式的運用
GitHubService service = retrofit.create(GitHubService.class);
[code]Call
本文關鍵詞:設計模式解析,由筆耕文化傳播整理發(fā)布。
本文編號:236203
本文鏈接:http://sikaile.net/wenshubaike/mishujinen/236203.html