1.前言
前面講的模式提供了一種在創(chuàng)建對(duì)象的同時(shí)隱藏創(chuàng)建邏輯的方式唬血,而不是直接實(shí)例化對(duì)象全陨。但結(jié)構(gòu)型模式關(guān)注的是如何組合類和對(duì)象痴晦,來獲取作用更廣泛的結(jié)構(gòu)或者新的功能。
結(jié)構(gòu)型模式分為類模式和對(duì)象模式花嘶。前者通過繼承類和實(shí)現(xiàn)接口的方式笋籽,使目標(biāo)類具備所有父類和接口的性質(zhì);后者則將一些已經(jīng)存在的對(duì)象組合起來椭员,擴(kuò)展了功能车海,同時(shí)可以動(dòng)態(tài)改變組合關(guān)系,具有很大的靈活性隘击。
2.概念
代理模式為其它對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問侍芝。當(dāng)無法或不想直接訪問某個(gè)對(duì)象時(shí),通過一個(gè)代理對(duì)象來間接訪問埋同。根據(jù)適用范圍分為四類(引用自《Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)》):
- 遠(yuǎn)程代理:為某個(gè)對(duì)象在不同的內(nèi)存地址空間提供局部代理州叠。
- 虛擬代理:代理一個(gè)十分消耗資源的對(duì)象,并在真正需要時(shí)才創(chuàng)建此對(duì)象凶赁。
- 保護(hù)代理:使用代理控制對(duì)原始對(duì)象的訪問咧栗。
- 智能引用:在訪問原始對(duì)象時(shí)執(zhí)行一些自己的附加操作逆甜。
3.場(chǎng)景
有張三、李四兩人分別想要租房和買房致板,但是他們都太忙了交煞,共同約了一個(gè)中介全權(quán)處理。需要注意的是斟或,租房有租房的流程素征,買房有買房的手續(xù)。
4.寫法
租房的房源較多萝挤,中介決定先幫張三處理好御毅。
// 1.聲明需要代理的操作
public interface Tenant {
void rent();
}
// 2.被代理者實(shí)現(xiàn)操作
public class ZhangSan implements Tenant {
@Override
public void rent() {
System.out.println("我要租這套房");
}
}
// 3.代理者調(diào)用被代理者的方法
public class Middleman implements Tenant {
private Tenant customer;
public Middleman(Tenant customer) {
super();
this.customer = customer;
}
@Override
public void rent() {
customer.rent();
}
}
public class Main {
public static void main(String[] args) {
// 租房子由中介出面
Tenant zhangSan = new ZhangSan();
Tenant middleMan = new Middleman(zhangSan);
middleMan.rent();
}
}
中介在替張三找房時(shí),又發(fā)現(xiàn)了一個(gè)非常適合李四的房源在出售怜珍。但是他現(xiàn)在走的是租憑流程亚享,想去辦理買房手續(xù)不全,顯得很難辦绘面。
public interface Buyer {
void buy();
}
public class LiSi implements Buyer {
@Override
public void buy() {
System.out.println("我要買這套房");
}
}
雖然也可以讓Middleman類實(shí)現(xiàn)Buyer接口獲取買房的方法,但是代理者同時(shí)具有兩個(gè)被代理者的方法侈沪,容易產(chǎn)生混淆揭璃,導(dǎo)致代理過程不透明,不符合代理模式設(shè)計(jì)的愿望亭罪。
上面這種方式瘦馍,由于代碼運(yùn)行前,代理者的.class編譯文件已存在应役,即代理關(guān)系已確定情组,所以稱之為靜態(tài)代理。如果不聲明代理者就可以不確定代理關(guān)系箩祥,從而在執(zhí)行階段決定代理誰院崇,需要用到Java的反射機(jī)制動(dòng)態(tài)地生成代理者對(duì)象,這便是動(dòng)態(tài)代理袍祖。與原型模式類似底瓣,Java也提供了一個(gè)動(dòng)態(tài)代理接口InvocationHandler,通過重寫 invoke() 方法實(shí)現(xiàn)動(dòng)態(tài)調(diào)用被代理者的方法蕉陋。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object customer;
public DynamicProxy(Object customer) {
super();
this.customer = customer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1.通過反射調(diào)用被代理對(duì)象的相應(yīng)方法
Object result = method.invoke(customer, args);
return result;
}
}
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
Tenant zhangSan = new ZhangSan();
Buyer liSi = new LiSi();
// 分別獲得兩人的全權(quán)代理
DynamicProxy tenant = new DynamicProxy(zhangSan);
DynamicProxy buyer = new DynamicProxy(liSi);
// 2.通過ClassLoader加載業(yè)務(wù)接口和代理對(duì)象捐凭,生成新的代理對(duì)象
Tenant zsProxy = (Tenant) Proxy.newProxyInstance(Tenant.class.getClassLoader(), new Class[] {Tenant.class}, tenant);
zsProxy.rent();
Buyer lsProxy = (Buyer) Proxy.newProxyInstance(Buyer.class.getClassLoader(), new Class[] {Buyer.class}, buyer);
lsProxy.buy();
}
}
雖然靜態(tài)代理符合面向?qū)ο蟮脑瓌t,將對(duì)象關(guān)系聲明清楚凳鬓,但是不夠靈活茁肠,需要聲明多個(gè)代理對(duì)象。而動(dòng)態(tài)代理缩举,將代理者與被代理者解耦垦梆,使代理者不受被代理者變化影響匹颤,令模式結(jié)構(gòu)清晰、簡(jiǎn)單奶赔。
5.總結(jié)
代理模式是之所以稱為結(jié)構(gòu)型模式惋嚎,是因?yàn)檫@種結(jié)構(gòu)為對(duì)象的訪問增加了獨(dú)立的處理空間,有利于添加相應(yīng)的功能站刑。就拿智能引用來說另伍,將它優(yōu)化一下,就是裝飾模式了绞旅。