代理模式簡單介紹
代理模式也稱爲委託模式,是結構型設計模式之一。那麼何爲代理呢?其實代理在我們日常生活中也並不少見,對於程序員來說最常接觸的莫過於代理上網了,連上代理服務器地址,就可以輕鬆暢遊全世界的網絡;還有每天吃飯時趕進度是常事,叫公司的同時幫忙買飯也是一種代理等等…總而言之,也許你並不留意,但是代理的確是無處不在,現實生活中如此,我們的 Code 世界裏也是如此!既然如此,我們來深究一下代理模式到底有多普遍。首先還是來看一下它的定義。
代理模式的定義
爲其他對象提供一種代理以控制對這個對象的訪問。
代理模式的使用場景
當無法或不想直接訪問某個對象或訪問某個對象存在困難時可以通過一個代理對象來間接訪問,爲了保證客戶端使用的透明性,委託對象與代理對象需要實現相同的接口。
代理模式的 UML 類圖
角色介紹:
- Subject:抽象主題類,該類的主要職責是聲明真實主題與代理的共同接口方法,該類既可以是一個抽象類也可以是一個接口。
- RealSubject:真實主題類,該類也稱爲被委託類或被代理類,該類定義了代理所表示的真實對象,由其執行具體的業務邏輯方法,而客戶類則通過代理類間接地調用真實主題類中定義的方法。
- ProxySubject:代理類,該類也稱爲委託類或代理類,該類持有一個對真實主題類的引用,在其所實現的接口方法中調用真實主題類中相應的接口方法執行,以此起到代理的作用。
- Client:客戶類,即使用代理類的類型。
根據類圖可以得出如下一個代理模式的通用模式代碼:
/**
* 抽象主題類
*/
public abstract class Subject {
/**
* 一個普通的業務方法
*/
public abstract void visit();
}
/**
* 實現抽象主題的真實主題類,也就是被代理類
*/
public class RealSubject extends Subject {
@Override
public void visit() {
//RealSubject 中 visit 的具體邏輯
Log.d("Subject", "Real subject!");
}
}
/**
* 代理類
*/
public class ProxySubject extends Subject {
private RealSubject mSubject;
public ProxySubject(RealSubject subject) {
this.mSubject = subject;
}
@Override
public void visit() {
//通過真實主題引用的對象調用真實主題中的邏輯方法
mSubject.visit();
}
}
/**
* 客戶類
*/
public class Client {
public static void main() {
//構造一個真實主題對象
RealSubject real = new RealSubject();
//通過真實主題對象構造一個代理對象
ProxySubject proxy = new ProxySubject(real);
//調用代理的相關方法
proxy.visit();
}
}
代理模式實戰
代理模式就如咱們開頭所說,你可以在日常生活中找到很多很多,比如:小民以前在公司上班時,就遇到過被老闆拖欠工資甚至剋扣工資的情況,這種情況下小民還是通過法律的途徑來解決問題,一旦小民選擇了走法律途徑解決該糾紛,那麼不可避免地就需要請一個律師類作爲自己的訴訟代理人,我們將訴訟的流程抽象在一個接口類中。
/**
* 訴訟接口類
*/
public interface ILawsuit {
//提交申請
void submit();
//進行舉證
void burden();
//開始辯護
void defend();
//訴訟完成
void finish();
}
4 個方法非常簡單,都是訴訟的一般流程。
/**
* 具體訴訟人
*/
public class XiaoMin implements ILawsuit {
@Override
public void submit() {
//老闆欠小民工資,小民只好申請仲裁
Log.d("ILawsuit", "老闆拖欠工資,特此申請仲裁!");
}
@Override
public void burden() {
//小民證據充足,不怕告不贏
Log.d("ILawsuit", "這是合同書和過去一年的銀行工資流水!");
}
@Override
public void defend() {
//鐵證如山,變化也沒有什麼好說的
Log.d("ILawsuit", "證據確鑿,不需要再說什麼了");
}
@Override
public void finish() {
//結果也是肯定的,必贏
Log.d("ILawsuit", "訴訟成功,判決老闆即日起七天內結算工資");
}
}
如上所訴,該類實現 ILawsuit 並對其中 4 個方法做出具體的實現邏輯,邏輯很簡單,都只是輸出一段話而已,當然,小民自己是不會去打官司的,於是小民請個律師代替自己進行訴訟。
public class Lawyer implements ILawsuit {
private ILawsuit mLawsuit; //持有一個具體被代理者的引用
public Lawyer(ILawsuit lawsuit) {
this.mLawsuit = lawsuit;
}
@Override
public void submit() {
mLawsuit.submit();
}
@Override
public void burden() {
mLawsuit.burden();
}
@Override
public void defend() {
mLawsuit.defend();
}
@Override
public void finish() {
mLawsuit.finish();
}
}
律師類表示代理者律師,在該類裏面會持有一個被代理者的引用,律師所執行的方法實質就是簡單地調用被代理者中的方法,下面來看看客戶類中具體的調用執行關係。
public class Client {
public static void main() {
//構造一個小民
ILawsuit xiaomin = new XiaoMin();
//構造一個代理律師並將小民作爲構造參數傳遞進去
ILawsuit lawyer = new Lawyer(xiaomin);
//律師提交訴訟申請
lawyer.submit();
//律師進行舉證
lawyer.burden();
//律師代替小民進行辯護
lawyer.defend();
//完成訴訟
lawyer.finish();
}
}
運行結果這裏不在給出,大家可以看到,其實代理模式也很簡單,其主要還是一種委託機制,真實對象將方法的執行委託給代理對象,而且委託的乾淨利落毫不做作,這也是爲什麼代理模式也稱爲委託模式的原因,相信大家不難理解。除此之外,大姐可以繼續發散思維,其實我們的代理類完全可以代理多個被代理類,就像上面的例子一樣,一個律師可以代理多個人大官司,這裏是沒有任何問題的,而具體到底是代理的哪個人,這就要看代理類中所持有的實際對象類型,上述的例子中實際對象是 XiaoMin,也就是代理的 XiaoMin.
代理其他人可以麼?當然可以,比如小輝,我們只需要在定義一個 XiaoHui 類實現 ILawsuit 即可,再在客戶類中國修改高層模塊調用邏輯,完全沒問題。
代理模式可以大致分爲兩大部分,一是靜態代理,二是動態代理。靜態代理如上述示例那樣,代理者的代碼有程序員自己或通過一些自動化工具生成固定的代碼在對其進行編譯,也就是說在我們的代碼運行前代理類的 class 編譯文件就已存在;而動態代理則與靜態代理相反,通過反射機制動態地生成代理者的對象,也就是說我們在 code 階段壓根就不需要知道代理誰,代理誰我們將會在執行階段決定。而 Java 也給我們提供了一個便捷的動態代理接口 InvocationHandler,實現該接口重寫其調用方法 invoke。
public class DynamicProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
在這裏,我們主要通過 invoke 方法來調用具體的被代理方法,也就是真實的方法。動態代理可以使我們的代碼邏輯更簡潔,不過在這之前我們得首先完善動態代理類。
public class DynamicProxy implements InvocationHandler {
private Object obj; //被代理類的類引用
public DynamicProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//調用被代理類對象的方法
Object result = method.invoke(obj,args);
return result;
}
}
如上述代碼所訴,我們聲明一個 Object 的引用,該引用將指向被代理類,而我們調用被代理類的具體方法則在 invoke 方法中執行,是不是很簡潔?也就是說我們原來由代理類所做的工作現在由 InvocationHandler 來處理,不在需要關係到底代理誰。下面我們修改客戶類的邏輯。
public class Client {
public static void main(){
//構造一個小民
ILawsuit xiaomin = new XiaoMin();
//構造一個動態代理
DynamicProxy proxy = new DynamicProxy(xiaomin);
//獲取被代理類小民的 ClassLoader
ClassLoader classLoader = xiaomin.getClass().getClassLoader();
//動態構造一個代理者律師
ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(classLoader,new Class[]{ILawsuit.class},proxy);
//律師提交訴訟申請
lawyer.submit();
//律師進行舉證
lawyer.burden();
//律師代替小民進行辯護
lawyer.defend();
//完成訴訟
lawyer.finish();
}
}