設計模式--適配器模式(JDK中的應用)

本來想開始進行java集合框架的學習,但是看到集合框架中用到了適配器模式,所以先學習一下此設計模式,能讓我們對java集合框架的設計思想理解更深入。JDK源碼中應用了很多設計模式,在學習源碼的遇到的時候會一一進行學習。閒言少敘,開始適配器模式。

適配器模式定義

適配器模式定義是:適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。其別名爲包裝器(Wrapper)。 解釋適配器很多教材和博客都採用電源適配器這個例子:比如中國和美國的電源插座不匹配,怎麼給手機充電呢,這就需要一個適配器,來進行適配。
適配器模式既可以作爲類結構型模式,也可以作爲對象結構型模式。 結構型模式(Structural Pattern)描述如何將類或者對 象結合在一起形成更大的結構,就像搭積木,可以通過簡單積木的組合形成複雜的、功能更爲強大的結構。結構型模式可以分爲類結構型模式和對象結構型模式:1. 類結構型模式關心類的組合,由多個類可以組合成一個更大的系統,在類結構型模式中一般只存在繼承關係和實現關係。 2. 對象結構型模式關心類與對象的組合,通過關聯關係使得在一 個類中定義另一個類的實例對象,然後通過該對象調用其方法。 根據“合成複用原則”,在系統中儘量使用關聯關係來替代繼承關係(多用組合少用繼承),因此大部分結構型模式都是對象結構型模式。

這時候我們可以引出一個原則:對類的功能的擴展,要多用組合,少用繼承。對於類的擴展,在面向對象的編程過程中,我們首先想到的是類的繼承,由子類繼承父類,從而完成了對子類功能的擴展。但是,面向對象的原則告訴我們,對類的功能的擴展要多用組合,而少用繼承。詳細的介紹可以查看這篇博客:組合還是繼承,這是一個問題。此博客舉的例子很好,易於理解組合相對於繼承的優點。

適配器模式的UML圖

畫出設計模式對於的uml圖是學習設計模式的基本要求,面試中也會讓我們畫出設計模式的uml圖,進行解釋。
模式所涉及的角色有:
  ● 目標(Target)角色:這就是所期待得到的接口。注意:由於類適配器模式,因此目標需是接口。
  ● 源(Adapee)角色:現在需要適配的接口。
  ● 適配器(Adaper)角色:適配器類是本模式的核心。適配器把源接口轉換成目標接口。顯然,這一角色不可以是接口,而必須是具體類。

類適配器模式

這裏寫圖片描述
此圖Adaper實現了Adapee,同時實現了Target接口。Adapter與Adaptee是繼承關係,這決定了這個適配器模式是類的。
衝突:Target期待調用Request方法,而Adaptee並沒有(這就是所謂的不兼容了)。
解決方案:爲使Target(電腦需要使用的中國的插座接口)能夠使用Adaptee(美國提供的插座口)類裏的SpecificRequest方法,故提供一箇中間環節Adapter(插座轉換器)類(繼承Adaptee & 實現Target接口),把Adaptee的API與Target的API銜接起來(適配)。這也是精髓,能夠使target與adpatee由無關係不適配的情況,轉化爲可以調用。
我們客戶端要使用的是target接口,是期待的接口,而提供實現的是adaptee。
其適配器代碼爲:

//適配器Adapter繼承自Adaptee,同時又實現了目標(Target)接口。
public class Adapter extends Adaptee implements Target {

    //目標接口要求調用Request()這個方法名,但源類Adaptee沒有方法Request()
    //因此適配器補充上這個方法名
    //但實際上Request()只是調用源類Adaptee的SpecificRequest()方法的內容
    //所以適配器只是將SpecificRequest()方法作了一層封裝,封裝成Target可以調用的Request()而已
    @Override
    public void Request() {
        this.SpecificRequest();
    }

}

客戶端(電腦電源線)可以通過下邊調用:

public class AdapterPattern {

    public static void main(String[] args){

        Target mAdapter = new Adapter();
        mAdapter.Request();  //這就調用的是adaptee的實現方法
    }
}

對象適配器模式

與類的適配器模式一樣,對象的適配器模式把被適配的類的API轉換成爲目標類的API,與類的適配器模式不同的是,對象的適配器模式不是使用繼承關係連接到Adaptee類,而是使用委派關係(組合形式)連接到Adaptee類。
這裏寫圖片描述
這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是組合關係,這決定了適配器模式是對象的。
衝突:Target期待調用Request方法,而Adaptee並沒有(這就是所謂的不兼容了)。
解決方案:爲使Target能夠使用Adaptee類裏的SpecificRequest方法,故提供一箇中間環節Adapter類(包裝了一個Adaptee的實例),把Adaptee的API與Target的API銜接起來(適配)。

class Adapter implements Target{  
    // 直接關聯被適配類  
    private Adaptee adaptee;  

    // 可以通過構造函數傳入具體需要適配的被適配類對象  
    public Adapter (Adaptee adaptee) {  
        this.adaptee = adaptee;  
    }  

    @Override
    public void Request() {  
        // 這裏是使用委託的方式完成特殊功能  
        this.adaptee.SpecificRequest();  
    }  
}  

客戶端調用:

public class AdapterPattern {
    public static void main(String[] args){
        //需要先創建一個被適配類的對象作爲參數  
        Target mAdapter = new Adapter(new Adaptee());
        mAdapter.Request();

    }
}

類適配器使用對象繼承的方式,是靜態的定義方式;而對象適配器使用對象組合的方式,是動態組合的方式。建議儘量使用對象適配器的實現方式,多用合成/聚合、少用繼承

適配器模式的優缺點

優點:
複用性:系統需要使用現有的類,而此類的接口不符合系統的需要。那麼通過適配器模式就可以讓這些功能得到更好的複用。
擴展性:在實現適配器功能的時候,可以調用自己開發的功能,從而自然地擴展系統的功能。
缺點:
過多的使用適配器,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是A接口,其實內部被適配成了B接口的實現,一個系統如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接對系統進行重構。

缺省適配模式

缺省適配器模式是適配器模式的一個特例。接口和抽象類結合,產生了優雅的缺省適配模式。當不需要全部實現接口提供的方法時,可先設計一個抽象類實現接口,併爲該接口中每個方法提供一個默認實現(或空方法),那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求,它適用於一個接口不想使用其所有的方法的情況。因此也稱爲單接口適配器模式(也叫做缺省適配器模式)。在java集合框架中,有大量的abstract抽象類。就是利用了此設計模式。
這裏寫圖片描述
(注意上邊的uml圖實現接口應該用虛線)

適配器模式在很多框架和項目中使用很廣泛,比如在jdk源碼中,有:

java.util.Arrays#asList()
java.io.InputStreamReader(InputStream)   //字符流和字節流的轉換
java.io.OutputStreamWriter(OutputStream)
javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()

都使用了適配器模式,具體可以看適配器模式原理及實例介紹 。介紹了一下應用。閱讀源碼時,當我們瞭解了其設計模式,將會理解更加深刻。

發佈了53 篇原創文章 · 獲贊 119 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章