序言
在以前學習適配器模式時,有個經典例子:就是有些電器的工作電壓不是220V, 比如電腦工作電壓20V,但是我們家庭用電的電壓是220。怎麼讓20V的電腦在220V的電壓下工作,這就需要一個電源適配器——俗稱充電器或變壓器。有了這個電源適配器 家庭的用電電壓跟電腦的用電電壓 即可兼容。
適配器模式
定義: 將一個接口轉換成客戶所希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名爲爲包裝器(Wrapper)。適配器模式既可以作爲類結構性模式,也可以作爲對象型結構性模式。
- Target(目標抽象類): 目標抽象類定義客戶所需的接口,可以是一個抽象類或接口,也可以是一個具體的類。
- Adapter(適配器類): 適配器可以調用另外一個接口,作爲一個轉換器,對Adaptee和Target進行適配,適配器類是適配器模式的核心。
- Adaptee(適配者類) : 適配者即被適配的角色,它定義了已存在的接口,這個接口需要適配。一般是一個具體的類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的源代碼。
在適配器模式結構圖我們可以看到 Adapter(適配器)和Adaptee(適配者)是有一種關聯的,這種關聯可以是繼承關係,也可以是一種組合關係。 繼承關係的我們一般稱爲類適配器模式 ; 組合關係的稱爲對象適配器模式
類適配器模式
定義一個需要被適配的類
/**
* @program
* @Desc Adaptee(適配者)
* @Author 遊戲人日常
* @CreateTime 2019/07/08--14:49
*/
public class Adaptee {
public void specificRequest(){
System.out.println("將需要被適配的方法");
}
}
定義一個目標接口
/**
* @program
* @Desc 目標接口
* @Author 遊戲人日常
* @CreateTime 2019/07/08--14:53
*/
public interface Target {
public void request();
}
定義適配器類, 因爲是類適配器模式,所以我們需要繼承Adaptee類。
/**
* @program
* @Desc Adapter(適配者)
* @Author 遊戲人日常
* @CreateTime 2019/07/08--14:57
*/
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
@Override
public void specificRequest() {
//這裏可以加下其他操作
super.specificRequest();
//這裏可以加下其他操作
}
}
客戶端測試
public static void main(String args []){
Target adapter=new Adapter();
adapter.request();
}
輸出結果 :
將需要被適配的方法
這裏我們可以看到適配器(Adapter)繼承了適配者(Adaptee),然後實現了目標接口(Target)。 這樣使目標接口跟適配者的接口就關聯起來了, 客戶端通過調用適配器的方法,從而達到調用了適配者所被適配的方法。
對象適配器模式
對象適配器模式跟類適配器模式所不同的就是適配器(Adapter)類是基於繼承的,而對象適配器是基於組合的。 其他的Target、Adaptee不變。
定義適配器類
/**
* @program
* @Desc Adapter(適配者)
* @Author 遊戲人日常
* @CreateTime 2019/07/08--14:57
*/
public class Adapter implements Target {
private Adaptee adaptee =new Adaptee();
@Override
public void request() {
adaptee.specificRequest();
}
}
客戶端測試
public static void main(String args []){
Target adapter=new Adapter();
adapter.request();
}
輸出結果 :
將需要被適配的方法
一般常用的就是對象適配器模式,很少用類適配器。 還有一種雙向適配器模式,就是(Adapter)適配器包含對目標類(Target)和適配者類(Adaptee)兩個引用。目標類可以通過適配器(Adapter)調用適配者(Adaptee中的方法, 適配者類也可以通過適配器調用目標類的方法。 UML類圖如下:
適配器(Adapter)類一般寫法如下:
/**
* @program
* @Desc Adapter(適配者)
* @Author 遊戲人日常
* @CreateTime 2019/07/08--14:57
*/
public class Adapter implements Target , Adaptee {
//同時對適配者和目標類的引用
private Target target;
private Adaptee adaptee;
public Adapter(Target target) {
this.target = target;
}
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
@Override
public void specificRequest() {
target.request();
}
}
在實際的開發中很少使用雙向適配器。
缺省適配器
缺省適配器模式是適配器模式一種變體。
定義: 當不需要實現一個接口所提供的所有方法時,可以設計一個抽象類實現該接口,併爲每個接口提供一個默認實現,那麼該抽象類的子類可以選擇性覆蓋父類中的方法,它適用於不想使用一個接口中的所有方法的情況,又稱爲單接口適配器模式。
簡單的理解就是當適配者(Adaptee)有大量的方法時,那麼每個適配器(Adapter)都要去實現接口中的這些方法,這樣就感覺太費勁。 所以這種情況下,可以考慮實現個默認的適配器,然後根據實際目標角色接口的類集成這個默認適配器,然後選擇性的實現默認適配器中的一些方法。
缺省適配器模式結構如下圖:
- ServiceInterface(適配者接口) : 是一個接口,裏面包含大量的方法。
- AbstractServiceClass(缺省適配器類) : 實現了ServiceInterface中聲明的方法, 通常定義爲抽象類。
- ConcreteServiceClass(具體業務類) : 它是缺省適配器的子類,在沒有引用適配器之前,它需要實現適配者(ServiceInterface)所有的方法, 有了缺省適配器類後,就可以有選擇性的覆蓋適配器類中的方法。
總結
適配器模式總共列出四種: 類適配器模式、 對象適配器模式 、 雙向適配器模式 、 缺省適配器模式。
- 類適配器模式 : 適配器(Adapter)跟適配者(Adaptee)是一種繼承關係。
- 對象適配器模式: 適配器(Adapter)跟適配者(Adaptee)是一種組合關係。
- 雙向適配器模式 : 雙向適配器是對象適配器模式的一種變體, 該模式的適配器(Adapter)不僅引用了適配者(Adaptee),還對目標類(Target)引用。 所以這種模式目標類和適配者之間互相適配。
- 缺省適配器模式: 當適配者接口中出現大量的方法時,就可以考慮這種模式。
適配器優缺點:
優點 :
類適配器模式和對象適配器模式都具有的優點:
- 將目標類(Target)和 適配者類(Adaptee)解耦, 引用一個適配器類(Adapter)就可以對適配者進行重用。
- 增加了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對客戶端來而言是透明的,而且提高了適配者的複用性,同一個適配者類可以在多個不同的系統中複用。
對象適配器模式優點:
- 一個對象適配器可以把多個適配者適配到同一目標。
- 可以適配一個適配者的子類,由於適配器和適配者之間是組合關係,根據”里氏代換原則“
,適配者的子類也可以通過該適配器進行適配。
缺點:
類適配器模式缺點:
- 因爲Java不支持多重繼承,一次最多適配一個適配者類,不能同時適配多個適配者。
- 適配者類不能爲最終類,即適配者類不能用final關鍵字修飾。
- 類適配器模式中的目標類只能爲接口,不能爲類。
對象適配器模式缺點:
- 與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩(因爲類適配器模式是基於繼承的,可以重寫適配者類的方法,對象適配器模式則不能),如果一定要置換掉適配者類的方法,可以先做一個適配者的子類,將適配者類的方法置換掉,然後再把適配者的子類當作真正的適配者進行適配,實現過程較爲複雜。
適用場景
- 系統需要使用一些現有的類,而這些類的接口(如方法名)不符合需要。
- 想創建一個可以重用的類,用於與一些彼此之間沒有太大的關聯的一些類,包括一些可能再將來引進的類一起工作。
參考