生活中的設(shè)計(jì)模式之適配器模式

定義

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)

avatar

目標(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)接口涡驮。

avatar

//適配者
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ì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耕蝉,一起剝皮案震驚了整個(gè)濱河市崔梗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垒在,老刑警劉巖蒜魄,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異场躯,居然都是意外死亡谈为,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門踢关,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伞鲫,“玉大人,你說我怎么就攤上這事签舞★跖В” “怎么了柒瓣?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吠架。 經(jīng)常有香客問我芙贫,道長(zhǎng),這世上最難降的妖魔是什么诵肛? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任屹培,我火速辦了婚禮,結(jié)果婚禮上怔檩,老公的妹妹穿的比我還像新娘褪秀。我一直安慰自己,他們只是感情好薛训,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布媒吗。 她就那樣靜靜地躺著,像睡著了一般乙埃。 火紅的嫁衣襯著肌膚如雪闸英。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天介袜,我揣著相機(jī)與錄音甫何,去河邊找鬼。 笑死遇伞,一個(gè)胖子當(dāng)著我的面吹牛辙喂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鸠珠,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼巍耗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了渐排?” 一聲冷哼從身側(cè)響起炬太,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驯耻,沒想到半個(gè)月后亲族,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡可缚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年孽水,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片城看。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡女气,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出测柠,到底是詐尸還是另有隱情炼鞠,我是刑警寧澤缘滥,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站谒主,受9級(jí)特大地震影響朝扼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜霎肯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一擎颖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧观游,春花似錦搂捧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搪柑,卻和暖如春聋丝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背工碾。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工弱睦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渊额。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓况木,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親端圈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子焦读,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容