前言
今天介紹適配器模式,舉個生活中的例子,我們筆記本用的到充電器其實就是個適配器,筆記本電腦的工作電壓是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ù)之巔上有你也有我。