設(shè)計模式干貨系列:(七)適配器模式【學(xué)習(xí)難度:★★☆☆☆,使用頻率:★★★★☆】

前言

今天介紹適配器模式,舉個生活中的例子,我們筆記本用的到充電器其實就是個適配器,筆記本電腦的工作電壓是20V,而我國的家庭用電是220V,如何讓20V的筆記本電腦能夠在220V的電壓下工作?就是靠這個充電器搞定的。

在軟件開發(fā)中,有時也存在類似這種不兼容的情況,我們也可以像引入一個電源適配器一樣引入一個稱之為適配器的角色來協(xié)調(diào)這些存在不兼容的結(jié)構(gòu),這種設(shè)計方案即為適配器模式。

正文

適配器模式概念

適配器模式(Adapter Pattern):將一個接口轉(zhuǎn)換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。適配器模式既可以作為類結(jié)構(gòu)型模式,也可以作為對象結(jié)構(gòu)型模式。

適配器模式結(jié)構(gòu)圖

適配器模式有類適配器模式和對象的適配器模式兩種不同的形式。如下圖所示,左邊是的類的適配器模式(繼承),右邊是對象的適配器模式(引用)。


類的適配器模式

類的適配器模式把被適配的類的API轉(zhuǎn)換成目標(biāo)類的API,其靜態(tài)結(jié)構(gòu)圖如下所示。



在上圖中可以看出,Adaptee類并沒有simpleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,提供一個中間環(huán)節(jié),即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關(guān)系,這決定了這個適配器模式是類的。

模式所涉及的角色有:

  • 目標(biāo)(Target)角色:這就是所期待得到的接口。注意,由于這里討論的是類適配器模式,因此目標(biāo)不可以是類。
  • 源(Adaptee)角色:現(xiàn)有需要配置的接口。
  • 適配器(Adapter)角色:適配器類是本模式的核心。適配器把源接口轉(zhuǎn)換成目標(biāo)接口。顯然,這一叫色不可以是接口,而必須是具體類。

對象的適配器模式

與類的適配器模式一樣,對象的適配器模式把被適配的類的API轉(zhuǎn)換成目標(biāo)類的API,與類的適配器模式不同的是,對象的適配器模式不是使用繼承關(guān)系連接到Adaptee類,而是使用委派關(guān)系連接到Adaptee類。對象的適配器模式的靜態(tài)結(jié)構(gòu)如下圖所示。



從上圖中可以看出,Adaptee類并沒有simpleOperation2()方法,而客戶端則期待這個方法。為使客戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關(guān)系,這決定了這個適配器模式是對象的。
從上圖可以看出,模式所涉及的角色有:

  • 目標(biāo)(Target)角色:這就是所期待的接口,目標(biāo)可以是具體的或抽象的類。
  • 源(Adaptee)角色:現(xiàn)有需要適配的接口。
  • 適配器(Adapter)角色:適配器類是本模式的核心。適配器把源接口轉(zhuǎn)換成目標(biāo)接口,顯然,這一角色必須是具體類。

代碼示例

類的適配器模式代碼

public interface Target {
    /**
     * 這是源類也有的方法simpleOperation1
     */
    void simpleOperation1();

    /**
     * 這是源類沒有的方法simpleOperation2
     */
    void simpleOperation2();

}

上面給出的是目標(biāo)角色的源代碼,這個角色是以一個Java接口的形式實現(xiàn)的。可以看出,這個接口聲明了兩個方法:simpleOperation1()和simpleOperation2()。而源角色Adatpee是一個具體類,它有一個simpleOperation1()方法,但是沒有simpleOperation2()方法,如下面代碼清單所示。

public class Adaptee {
    /**
     * 源類含有方法simpleOperation1
     */
    public void simpleOperation1(){};
}

適配器角色Adapter擴(kuò)展了Adaptee,同時又實現(xiàn)了目標(biāo)接口。由于Adaptee沒有提供simpleOperation2()方法,而目標(biāo)接口又要求這個方法,因此適配器角色Adatper實現(xiàn)了這個方法,如下面代碼清單所示。

public class Adapter extends Adaptee implements Target {

    /**
     * 由于源類沒有方法simpleOperation2
     * 因此適配器類補(bǔ)充上這個方法
     */
    @Override
    public void simpleOperation2() {

    }
}

類的適配器模式的效果

  • 使用一個具體類把源(Adaptee)適配到目標(biāo)(Target)中。這樣一來,如果源以及源的子類都使用此類適配,就行不通了。
  • 由于適配器類是源的子類,因此可以適配器類中之換掉(Override)源的一些方法。
  • 由于只引進(jìn)了一個適配器類,因此只有一個路線到目標(biāo)類,使問題得到簡化。

對象的適配器模式代碼

public interface Target {
    /**
     * 這是源類也有的方法simpleOperation1
     */
    void simpleOperation1();

    /**
     * 這是源類沒有的方法simpleOperation2
     */
    void simpleOperation2();
}

上面給出的是目標(biāo)角色的源代碼,這個角色是以一個Java接口的形式實現(xiàn)的。可以看出,這個接口聲明了兩個方法:simpleOperation1()和simpleOperation2()。而源角色Adapatee是一個具體類,它有一個simpleOperation1()方法,但是沒有simpleOperation2()方法,如下面帶入清單所示。

public class Adaptee {
    /**
     * 源類含有方法simpleOperation1
     */
    public void simpleOperation1(){};
}

適配器類的源代碼如下面代碼清單所示。

public class Adapter implements Target {


    private Adaptee adaptee;


    public Adapter(Adaptee adaptee) {
        super();
        this.adaptee = adaptee;
    }


    /**
     * 源類有方法simpleOperation1
     * 因此適配器類直接委派即可
     */
    @Override
    public void simpleOperation1() {
        adaptee.simpleOperation1();
    }


    /**
     * 源類沒有方法simpleOperation2
     * 因此適配器類補(bǔ)充上這個方法
     */
    @Override
    public void simpleOperation2() {
        //write you code here
    }
}

對象的適配器模式的效果

  • 一個適配器可以把多種不同的源適配到同一個目標(biāo)。換言之,同一個適配器可以把源類和它的子類都適配到目標(biāo)接口。
  • 與類的適配器模式相比,要想置換源類的方法就不容易。如果一定要置換掉源類的一個或多個方法,就只好先做一個源類的子類,將源類的方法置換掉,然后再把源類的子類當(dāng)作真正的源進(jìn)行適配。
  • 雖然想要置換源類的方法不容易,但是要想增加一些新的方法則方便得很,而且新增加的方法可同時適用于所有的源。

總結(jié)

適配器模式將現(xiàn)有接口轉(zhuǎn)化為客戶類所期望的接口,實現(xiàn)了對現(xiàn)有類的復(fù)用,它是一種使用頻率非常高的設(shè)計模式,在軟件開發(fā)中得以廣泛應(yīng)用,在Spring等開源框架、驅(qū)動程序設(shè)計(如JDBC中的數(shù)據(jù)庫驅(qū)動程序)中也使用了適配器模式。
** 1. 主要優(yōu)點(diǎn)**
無論是對象適配器模式還是類適配器模式都具有如下優(yōu)點(diǎn):

  • 將目標(biāo)類和適配者類解耦,通過引入一個適配器類來重用現(xiàn)有的適配者類,無須修改原有結(jié)構(gòu)。
  • 增加了類的透明性和復(fù)用性,將具體的業(yè)務(wù)實現(xiàn)過程封裝在適配者類中,對于客戶端類而言是透明的,而且提高了適配者的復(fù)用性,同一個適配者類可以在多個不同的系統(tǒng)中復(fù)用。
  • 靈活性和擴(kuò)展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎(chǔ)上增加新的適配器類,完全符合“開閉原則”。

具體來說,類適配器模式還有如下優(yōu)點(diǎn):

  • 由于適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強(qiáng)。

對象適配器模式還有如下優(yōu)點(diǎn):

  • 一個對象適配器可以把多個不同的適配者適配到同一個目標(biāo)
  • 可以適配一個適配者的子類,由于適配器和適配者之間是關(guān)聯(lián)關(guān)系,根據(jù)“里氏代換原則”,適配者的子類也可通過該適配器進(jìn)行適配。

** 2. 主要缺點(diǎn)**
類適配器模式的缺點(diǎn)如下:

  • 對于Java、C#等不支持多重類繼承的語言,一次最多只能適配一個適配者類,不能同時適配多個適配者
  • 適配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
  • 在Java、C#等語言中,類適配器模式中的目標(biāo)抽象類只能為接口,不能為類,其使用有一定的局限性。

對象適配器模式的缺點(diǎn)如下:

  • 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然后再把適配者類的子類當(dāng)做真正的適配者進(jìn)行適配,實現(xiàn)過程較為復(fù)雜。

** 3. 適用場景**
在以下情況下可以考慮使用適配器模式:

  • 系統(tǒng)需要使用一些現(xiàn)有的類,而這些類的接口(如方法名)不符合系統(tǒng)的需要,甚至沒有這些類的源代碼。
  • 想創(chuàng)建一個可以重復(fù)使用的類,用于與一些彼此之間沒有太大關(guān)聯(lián)的一些類,包括一些可能在將來引進(jìn)的類一起工作。
  • (對對象的適配器模式而言)在設(shè)計里,需要改變多個已有的子類的接口,如果使用類的適配器模式,就要針對每一個子類做一個適配器類,而這不太實際。

整理的適配器模式思維導(dǎo)圖

用思維導(dǎo)圖畫了份總結(jié),懶得看文章的也可以直接看圖片,另外需要源文件的童鞋可以關(guān)注我個人主頁上的公眾號,回復(fù)適配器模式即可獲取源文件網(wǎng)盤地址。


一直覺得自己寫的不是技術(shù),而是情懷,一篇篇文章是自己這一路走來的痕跡。靠專業(yè)技能的成功是最具可復(fù)制性的,希望我的這條路能讓你少走彎路,希望我能幫你抹去知識的蒙塵,希望我能幫你理清知識的脈絡(luò),希望未來技術(shù)之巔上有你也有我。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,908評論 2 372

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