適配器模式:
將一個接口轉換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名包裝器(Wrapper)。適配器模式,既可以作爲類結構型模式,也可以作爲對象結構型模式。
在適配器模式中,我們通過增加一個新的適配器類來解決接口不兼容的問題,使得原本沒有 任何關係的類可以協同工作。根據適配器類與適配者類的關係不同,適配器模式可分爲對象 適配器和類適配器兩種,在對象適配器模式中,適配器與適配者之間是關聯關係;在類適配 器模式中,適配器與適配者之間是繼承(或實現)關係。在實際開發中,對象適配器的使用頻率更高。
對象適配器模式:
在對象適配器結構圖中包含如下幾個角色:
- Target(目標抽象類):目標抽象類定義了客戶需要的接口,可以是一個抽象類或接口,也可以是具體類。
- Adapter(適配器類):適配器可以調用另一個接口,作爲一個轉換器,對Adaptee和Target進 行適配,適配器類是適配器模式的核心,在對象適配器中,它通過繼承Target並關聯一個 Adaptee對象使二者產生聯繫。
- Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口 需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的源代碼。
根據對象適配器模式結構圖,在對象適配器中,客戶端需要調用 request() 方法,而適配者類 Adaptee 沒有該方法,但是它所提供的 specificRequest() 方法卻是客戶端所需要的。爲了使客戶端能夠使用適配者類,需要提供一個包裝類Adapter,即適配器類。這個包裝類包裝了一個適 配者的實例,從而將客戶端與適配者銜接起來,在適配器的 request() 方法中調用適配者的 specificRequest() 方法。因爲適配器類與適配者類是關聯關係(也可稱之爲委派關係),所以這種適配器模式稱爲對象適配器模式。典型的對象適配器代碼如下所示:
class Adapter extends Target {
private Adaptee adaptee; //維持一個對適配者對象的引用
public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest(); //轉發調用
}
}
類適配器:
除了對象適配器模式之外,適配器模式還有一種形式,那就是類適配器模式,類適配器模式 和對象適配器模式最大的區別在於適配器和適配者之間的關係不同,對象適配器模式中適配 器和適配者之間是關聯關係,而類適配器模式中適配器和適配者是繼承關係,類適配器模式 結構如圖所示:
根據類適配器模式結構圖,適配器類實現了抽象目標類接口 Target,並繼承了適配者類,在適 配器類的 request() 方法中調用所繼承的適配者類的 specificRequest() 方法,實現了適配。
典型的類適配器代碼如下所示:
class Adapter extends Adaptee implements Target {
public void request() {
specificRequest();
}
}
由於Java、C#等語言不支持多重類繼承,因此類適配器的使用受到很多限制,例如如果目標 抽象類Target不是接口,而是一個類,就無法使用類適配器;此外,如果適配者Adapter爲最終 (Final)類,也無法使用類適配器。在Java等面向對象編程語言中,大部分情況下我們使用的是 對象適配器,類適配器較少使用。
總結:
1. 主要優點:
- 將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,無須修改原有 結構。
- 增加了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,而且提高了適配者的複用性,同一個適配者類可以在多個不同的系統中複用。
- 靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修 改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
2. 主要缺點:
類適配器模式的缺點如下:
-
對於Java、C#等不支持多重類繼承的語言,一次最多隻能適配一個適配者類,不能同時適 配多個適配者;
-
適配者類不能爲最終類,如在Java中不能爲final類,C#中不能爲sealed類;
-
在Java、C#等語言中,類適配器模式中的目標抽象類只能爲接口,不能爲類,其使用有一 定的侷限性。
對象適配器模式的缺點如下:
與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉 適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然 後再把適配者類的子類當做真正的適配者進行適配,實現過程較爲複雜。
適用場景:
- 系統需要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需要,甚至沒有 這些類的源代碼。
- 想創建一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。