1.代理模式的介紹
代理模式(Proxy Pattern)也稱為委托模式氓辣,是結(jié)構(gòu)型設(shè)計(jì)模式的一種拗踢。在我們生活中代理模式是非常常見的亲善,比如我們讓同事幫忙買飯,房屋中介等都是一種代理模式芥吟。也就是讓別人給自己干活侦铜。
2.代理模式的定義
為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。
3.代理模式的UML類圖
- Subject: 抽象角色指真實(shí)角色和代理角色共有的接口钟鸵。
- RealSubject: 真實(shí)角色需要實(shí)現(xiàn)抽象接口钉稍,定義真實(shí)角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯,以供代理角色調(diào)用棺耍。
- ProxySubject: 代理角色也需要實(shí)現(xiàn)抽象接口贡未,是真實(shí)角色的代理,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法蒙袍,并可以附加自己的操作俊卤。
4.靜態(tài)代理模式的實(shí)現(xiàn)
通過(guò)讓朋友代買水果,來(lái)實(shí)現(xiàn)代理模式:
第一步:先創(chuàng)建一個(gè)買水果的接口 (抽象角色)
package com.monkey.designmode.proxy;
/**
* Created by Monkey on 2020/5/9.
* Description: 買水果的接口
*/
public interface IBuyFruit {
void buyFruit();
}
第二步:實(shí)現(xiàn)買水果的接口 (真實(shí)角色)
package com.monkey.designmode.proxy;
/**
* Created by Monkey on 2020/5/9.
* Description: 具體的實(shí)現(xiàn)類害幅,實(shí)現(xiàn)買水果的接口
*/
public class Monkey implements IBuyFruit {
@Override
public void buyFruit() {
System.out.println("我在果園里買水果");
}
}
第三步:創(chuàng)建一個(gè)代理對(duì)象 (代理角色)
package com.monkey.designmode.proxy;
/**
* Created by Monkey on 2020/5/9.
* Description:代理類 持有一個(gè)被代理者的引用
*/
public class BuyFruitProxy implements IBuyFruit {
private IBuyFruit iBuy;
public BuyFruitProxy(IBuyFruit iBuy) {
this.iBuy = iBuy;
}
@Override
public void buyFruit() {
iBuy.buyFruit();
}
}
客戶端
package com.monkey.designmode.proxy;
public class ProxyClient {
public static void main(String[] args) {
IBuyFruit monkey = new Monkey();
BuyFruitProxy proxy = new BuyFruitProxy(monkey);
proxy.buyFruit();
}
}
這就是一個(gè)簡(jiǎn)單的靜態(tài)代理模式消恍,真實(shí)對(duì)象將方法的執(zhí)行委托給代理對(duì)象去完成。
5.動(dòng)態(tài)代理模式的實(shí)現(xiàn)
我們?cè)陟o態(tài)代理的基礎(chǔ)上實(shí)現(xiàn)動(dòng)態(tài)代理模式,在這里我們需要?jiǎng)?chuàng)建一個(gè)動(dòng)態(tài)處理器以现,幫我們創(chuàng)建一個(gè)代理類狠怨,而不需要靜態(tài)代理中的 BuyFruitProxy
代理類了。動(dòng)態(tài)代理是在運(yùn)行時(shí)利用反射的機(jī)制動(dòng)態(tài)生成代理者的對(duì)象邑遏。
創(chuàng)建一個(gè)動(dòng)態(tài)代理類
package com.monkey.designmode.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by Monkey on 2020/5/9.
* Description:動(dòng)態(tài)代理類
*/
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 {
//調(diào)用被代理對(duì)象的方法
Object result = method.invoke(object,args);
return result;
}
}
在動(dòng)態(tài)代理類中我們聲明一個(gè)Object
的引用佣赖,指向被代理類,我們調(diào)用被代理類的具體方法在invoke()
方法中執(zhí)行记盒。
客戶端
package com.monkey.designmode.proxy;
import java.lang.reflect.Proxy;
public class ProxyClient {
public static void main(String[] args) {
IBuyFruit monkey = new Monkey();
//構(gòu)造一個(gè)動(dòng)態(tài)代理
DynamicProxy dynamicProxy = new DynamicProxy(monkey);
ClassLoader classLoader = monkey.getClass().getClassLoader();
//動(dòng)態(tài)構(gòu)造一個(gè)代理者
IBuyFruit proxy = (IBuyFruit) Proxy.newProxyInstance(classLoader,
new Class[]{IBuyFruit.class},dynamicProxy);
proxy.buyFruit();
}
}
Proxy.newProxyInstance()方法接受三個(gè)參數(shù):
- ClassLoader loader:指定當(dāng)前目標(biāo)對(duì)象使用的類加載器,獲取加載器的方法是固定的
- Class<?>[] interfaces:指定目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型
- InvocationHandler:指定動(dòng)態(tài)處理器憎蛤,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法
使用動(dòng)態(tài)代理很好的對(duì)代理者與被代理者進(jìn)行解耦,使兩者之間沒有了直接的耦合;其中靜態(tài)代理只能對(duì)給定的接口下的實(shí)現(xiàn)類做代理孽鸡。而靜態(tài)代理更符合面向?qū)ο蟮脑瓌t蹂午。
6.總結(jié)
通過(guò)引入代理對(duì)象的方式間接訪問目標(biāo)對(duì)象,防止直接訪問目標(biāo)對(duì)象給系統(tǒng)帶來(lái)的不必要復(fù)雜性彬碱。通過(guò)代理對(duì)象對(duì)原有的業(yè)務(wù)增強(qiáng)豆胸,而不影響原有的業(yè)務(wù)。
我們用通俗易懂的方式來(lái)理解下靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別巷疼,我們?cè)谛枰獎(jiǎng)e人幫忙的時(shí)候晚胡,會(huì)提前給別人說(shuō)好之后,才會(huì)給我們幫忙嚼沿,這就是靜態(tài)代理模式估盘;而我們?cè)谛枰獛兔Φ臅r(shí)候,直接告訴別人骡尽,現(xiàn)在就給我?guī)兔η餐祝@就是動(dòng)態(tài)代理模式。