定義
The adapter pattern convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.
將一個(gè)類的接口轉(zhuǎn)換成客戶期望的另一個(gè)接口逗物,使原本不兼容的接口可以一起工作。
小故事
前不久宴猾,我開發(fā)了一套支付系統(tǒng)陡舅,可以支持用戶使用積分支付抵乓。不久后,新增了微信支付靶衍,但微信提供的支付SDK接口和我們系統(tǒng)的Pay接口不一樣灾炭,所以我便增加if...else語句來擴(kuò)展新的支付方式。再之后颅眶,又增加了支付寶......
public class Client {
public static void main(String[] args) {
if(args[0].equals("積分")){
PointPay pay = new PointPay();
pay.pay(args);
}else if(args[0].equals("微信")){
WXSDKPay wxsdkPay= new WXSDKPay();
wxsdkPay.payByWX(convert(args));
}else if(args[0].equals("支付寶")){
ALISDKPay alisdkPay= new ALISDKPay();
alisdkPay.payByALI(convert(args));
}
}
}
問題
故事中蜈出,積分支付、微信支付涛酗、支付寶三者的功能都是一樣的掏缎,但接口卻不一樣具體地說是操作(行為皱蹦、方法)不一樣。
當(dāng)兩個(gè)對(duì)象的功能一樣眷蜈,操作不一樣時(shí)沪哺,如果我們使用if...else這種差異化的處理方式操作對(duì)象,那么一旦發(fā)生擴(kuò)展就得修改客戶端的代碼酌儒,但如果不允許我們修改客戶端代碼辜妓,又怎么辦呢?
因此忌怎,為了避免上面的問題籍滴,我們應(yīng)該使用適配器模式——將不一樣的操作轉(zhuǎn)換成客戶端期望的統(tǒng)一操作。
方案
既然上面"差異化的處理方式"會(huì)導(dǎo)致很多問題榴啸,那么我們應(yīng)該統(tǒng)一處理方式孽惰。最直接的方式是讓適配者(微信支付)繼承目標(biāo)接口(Payment)這樣客戶就能統(tǒng)一操作這樣對(duì)象了。
但是鸥印,如果我們不能修改適配者呢勋功?那么我們可以新建一個(gè)實(shí)現(xiàn)目標(biāo)接口并組合了適配者的類,讓它將適配者的操作轉(zhuǎn)換成客戶端期望的操作库说,這個(gè)類被稱為適配器狂鞋,這種方式即是適配器模式。
public class WXPayAdapter implements Payment{
protected WXSDKPay wxsdkPay;
public WXPayAdapter(){
wxsdkPay = new WXSDKPay();
}
@Override
public void pay(String[] args) {
//將對(duì)象的payByWX操作轉(zhuǎn)換成客戶期望的pay操作
wxsdkPay.payByWX(convert(args));
}
}
在適配器模式中潜的,適配器位于客戶端和適配者中間骚揍,適配者對(duì)客戶端不可見,因此適配者本身的變化不會(huì)影響客戶端啰挪;適配器的接口和客戶端期望的接口一致信不,因此我們可以在不改變客戶端代碼的前提下,通過適配器復(fù)用已存在的適配者亡呵,前提是客戶端采用多態(tài)訪問目標(biāo)對(duì)象浑塞。
應(yīng)用
接下來,我們使用適配器模式重構(gòu)一下"支付系統(tǒng)"政己,使其可以復(fù)用與系統(tǒng)不兼容的對(duì)象酌壕。
首先,我們的支付系統(tǒng)已經(jīng)存在一個(gè)支付接口以及積分支付類歇由。
//支付接口
public interface Payment {
public void pay(String[] args);
}
//支付接口
public class PointPay implements Payment{
@Override
public void pay(String[] args) {
System.out.println("使用積分支付");
}
}
然后卵牍,為了擴(kuò)展新的支付方式WXSDKPay、ALISDKPay沦泌,我們需要為它們創(chuàng)建對(duì)應(yīng)的適配器糊昙。
/**微信支付適配器*/
public class WXPaymentAdapter implements Payment {
protected WXSDKPay wxsdkPay;
public WXPaymentAdapter(WXSDKPay wxsdkPay){
this.wxsdkPay = wxsdkPay;
}
@Override
public void pay(String[] args) {
wxsdkPay.payByWX(convert(args));
}
}
/**支付寶適配器*/
public class ALIPaymentAdapter implements Payment {
protected ALISDKPay alisdkPay;
public ALIPaymentAdapter(ALISDKPay alisdkPay){
this.alisdkPay = alisdkPay;
}
@Override
public void pay(String[] args) {
alisdkPay.payByALI(convert(args));
}
}
最后,我們?cè)诳纯纯蛻舳巳绾问褂眠m配器模式谢谦。
public class Client {
public static Payment getPayment(String method){
//這里讀者可以理解為動(dòng)態(tài)獲取的
PointPay pay = new PointPay();
ALIPaymentAdapter wxAdapter = new ALIPaymentAdapter(new WXSDKPay);
WXPaymentAdapter aliAdapter = new WXPaymentAdapter(new ALISDKPay);
return aliAdapter;
}
public static void main(String[] args) {
//操作方式是統(tǒng)一的
Payment pay = getPayment(args[0]);
pay.pay(args);
}
}
結(jié)構(gòu)
目標(biāo)接口(Target):聲明客戶端統(tǒng)一的操作以及適配器需要實(shí)現(xiàn)的接口释牺,萝衩。
適配者(Adaptee):是客戶端期望操作但與目標(biāo)接口不一致的對(duì)象。
適配器(Adapter):實(shí)現(xiàn)了目標(biāo)接口没咙,負(fù)責(zé)將適配者的操作轉(zhuǎn)成客戶期望的操作猩谊,它持有適配者。
客戶端(Client):負(fù)責(zé)操作目標(biāo)接口的對(duì)象祭刚,它不關(guān)心目標(biāo)接口的實(shí)現(xiàn)類是系統(tǒng)提供的還是由適配器包裝而成的牌捷。
實(shí)現(xiàn)類型
對(duì)象適配器
對(duì)象適配器(Object Adapter)通過組合適配者(Adaptee)和實(shí)現(xiàn)目標(biāo)接口(Target)的方式,給適配者新增目標(biāo)接口涡驮。
//適配者
public class Adaptee {
public void specificRequest(){}
}
//目標(biāo)接口
public interface Target {
public void request();
}
//1暗甥、實(shí)現(xiàn)Target
public class Adapter implements Target{
protected Adaptee adaptee;
//2、組合Adaptee
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void request() {
//3捉捅、轉(zhuǎn)換請(qǐng)求
adaptee.specificRequest();
}
}
public class Client {
public void main(String[] args){
//用適配器包裝適配者撤防,將其偽裝成目標(biāo)接口
Target target = new Adapter(new Adaptee());
//符合客戶的期望
target.request();
}
}
類適配器
類適配器(Class Adapter)和對(duì)象適配器主要的差異是,它通過繼承的方式而對(duì)象適配器通過組合的方式棒口。
//適配者
public class Adaptee {
public void specificRequest(){}
}
//目標(biāo)接口
public interface Target {
public void request();
}
//1寄月、繼承適配者;2陌凳、實(shí)現(xiàn)目標(biāo)接口
public class ClassAdapter extends Adaptee implements Target{
@Override
public void leaveRequest() {
//3、轉(zhuǎn)發(fā)請(qǐng)求——調(diào)用適配器繼承下來的方法specificRequest
specificRequest();
}
}
public class Client {
public void main(String[] args){
//客戶都不知道有適配者的存在
Target target = new ClassAdapter();
target.leaveRequest();
}
}
總結(jié)
當(dāng)一個(gè)對(duì)象的接口和系統(tǒng)的接口不一致内舟,而我們又想使用這個(gè)對(duì)象時(shí)合敦,那么我們可以使用適配器將這個(gè)對(duì)象包裝成與系統(tǒng)接口兼容的對(duì)象。
這樣验游,可以使我們?cè)诓恍薷南到y(tǒng)的前提下充岛,復(fù)用已經(jīng)存在的對(duì)象。