什么是代理模式呢苗桂,就是先創(chuàng)建一個(gè)接口,這個(gè)接口中定義了一些未實(shí)現(xiàn)的方法趴泌,再定義兩個(gè)類A舟舒、B來(lái)分別實(shí)現(xiàn)這些接口,其中類A用來(lái)實(shí)現(xiàn)具體的方法嗜憔,這被稱為目標(biāo)實(shí)現(xiàn)類秃励,而另一個(gè)類B是通過(guò)設(shè)置一些條件來(lái)判斷要不要來(lái)進(jìn)行調(diào)用A類的接口實(shí)現(xiàn),那么B被稱為代理類吉捶。就是說(shuō)我們可以通過(guò)代理類來(lái)間接訪問(wèn)目標(biāo)類夺鲜,下面我來(lái)舉一個(gè)例子:
人的基本需求是什么?買東西澳盘颉币励!所以我們來(lái)定義一個(gè)人的接口如下:
interface People{
public void buy();
}
好了,但是人的欲望是無(wú)窮的珊拼,但是如果你要買東西食呻,比如iphone,那么你的身上必須得有錢才行吧澎现,所以我們定義了一個(gè)有‘錢’這屬性的客戶
class Customer implements People{
private int money;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public void buy(){
System.out.println("買了一臺(tái)iphone");
}
}
我們看上面的代碼仅胞,我們就會(huì)發(fā)現(xiàn)一個(gè)關(guān)系沒(méi)有被關(guān)聯(lián)起來(lái),那就是金錢和買iphone之間的關(guān)系剑辫,并不是你有多少錢都可以買一個(gè)iphone的干旧,你得是土豪才可以,于是我們可以在buy()方法中增加如下的判斷語(yǔ)句
@Override
public void buy(){
if(money > 4000)
System.out.println("買了一臺(tái)iphone");
}
可是Apple公司會(huì)發(fā)布新的iphone揭斧,價(jià)格也是水漲船高莱革,那么我們就得不停地修改上面判斷條件峻堰,這樣的不停地改動(dòng)很麻煩,也不符合面向?qū)ο笾械拈_(kāi)閉原則盅视,即
需求的變更捐名,新的功能的添加應(yīng)該用對(duì)原有進(jìn)行拓展的方式進(jìn)行,而不是通過(guò)修改原有代碼來(lái)實(shí)現(xiàn)
所以我們維持Customer類的簡(jiǎn)單性闹击,而通過(guò)一個(gè)新增一個(gè)類來(lái)對(duì)上面的邏輯進(jìn)行處理镶蹋,這個(gè)類的功能是什么呢?首先檢查你的錢包赏半,假如你的錢包里面的鈔票不給力贺归,那對(duì)不起,你沒(méi)有資格購(gòu)買iphone断箫,這個(gè)充當(dāng)檢查錢包的類被稱為代理類拂酣,如下:
public class StaticProxy implements People{
People people;
public StaticProxy(People people){
this.people = people;
}
public void buy(){
if (((Customer)people).getMoney() > 4000)
people.buy();
else
System.out.println("你無(wú)法購(gòu)買iphone");
}
public static void test(){
Customer customer1 = new Customer();
customer1.setMoney(5000);
StaticProxy proxy = new StaticProxy(customer1);
proxy.buy();
}
}
那么新的iphone發(fā)布的時(shí)候,我們只要修改代理類中的判斷邏輯就可以了仲义,但是這種方式有沒(méi)有缺點(diǎn)呢婶熬,肯定的嘛,我們仔細(xì)看一下上面的代碼埃撵,代理類和目標(biāo)類都實(shí)現(xiàn)了People這個(gè)接口赵颅,假如People接口新增了方法,那么這兩個(gè)類都需要進(jìn)行添加方法的實(shí)現(xiàn)才可以暂刘,那么有沒(méi)有更省事的方法呢饺谬?答案是有的,那就是動(dòng)態(tài)代理谣拣。
動(dòng)態(tài)代理省去了代理類添加接口實(shí)現(xiàn)方法的過(guò)程募寨,那么它是怎么實(shí)現(xiàn)的呢,如下:
public class DynamicProxy {
private Object target;
public DynamicProxy(Object target){
this.target = target;
}
public Object getProxyInstance(){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object returnedValue = method.invoke(target,args);
return returnedValue;
}
});
}
}
上面的核心代碼是這句:
Object returnedValue = method.invoke(target,args);
其中的invoke方法芝发,它的作用是這樣的:
1绪商、我們通過(guò)反射根據(jù)類的方法名和方法參數(shù)來(lái)獲取一個(gè)對(duì)象所屬類的方法對(duì)象
2苛谷、根據(jù)方法對(duì)象的invoke方法輸入對(duì)象實(shí)例和參數(shù)值來(lái)獲取這個(gè)方法的輸出
具體的原理可看源代碼辅鲸,我這里不再贅述。好了腹殿,我們看一下測(cè)試代碼
People people = new Customer();
((Customer)people).setMoney(10000);
People proxy = (People) new DynamicProxy(people).getProxyInstance();
proxy.buy();
可以看到独悴,我們并沒(méi)有在動(dòng)態(tài)代理中實(shí)現(xiàn)這個(gè)接口的方法,所以利用動(dòng)態(tài)代理锣尉,我們?cè)黾咏涌谥械姆椒〞r(shí)只需要增加目標(biāo)類的實(shí)現(xiàn)即可刻炒。