設計模式---適配器模式

一.概述
適配器模式(Adapter Pattern):將一個接口轉換成客戶希望的另一個接口,使接口不兼容的那些類可以一起工作,其別名爲包裝器(Wrapper)。適配器模式既可以作爲類結構型模式,也可以作爲對象結構型模式。

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

在軟件開發中,有時也存在類似這種不兼容的情況,我們也可以像引入一個電源適配器一樣引入一個稱之爲適配器的角色來協調這些存在不兼容的結構,這種設計方案即爲適配器模式。

適配器模式有類適配器模式(繼承)和對象的適配器模式(引用)兩種不同的形式。

二.類的適配器模式
類的適配器模式把被適配的類的API轉換成目標類的API,其靜態結構圖如下所示。
在這裏插入圖片描述
在上圖中可以看出,Adaptee類並沒有simpleOperation2()方法,而客戶端則期待這個方法。爲使客戶端能夠使用Adaptee類,提供一箇中間環節,即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關係,這決定了這個適配器模式是類的。
模式所涉及的角色有:
目標(Target)角色:這就是期待得到的接口。注意,由於這裏討論的是類適配器模式,因此目標不可以是類。
源(Adaptee)角色:現有需要配置的接口。
適配器(Adapter)角色:適配器類是本模式的核心。適配器把源接口轉換成目標接口。顯然,這一角色不可以是接口,而必須是具體類。

代碼示例:

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

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

public class Adapter extends Adaptee implements Target {

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

    }
}

類的適配器模式的效果:
使用一個具體類把源(Adaptee)適配到目標(Target)中。這樣一來,如果源以及源的子類都使用此類適配,就行不通了。
由於適配器類是源的子類,因此可以在適配器類中換掉(Override)源的一些方法。
由於只引進了一個適配器類,因此只有一個路線到目標類,使問題得到簡化。

三.對象的適配器模式
與類的適配器模式一樣,對象的適配器模式把被適配的類的API轉換成目標類的API,與類的適配器模式不同的是,對象的適配器模式不是使用繼承關係連接到Adaptee類,而是使用委派關係連接到Adaptee類。對象的適配器模式的靜態結構如下圖所示。
在這裏插入圖片描述
從上圖中可以看出,Adaptee類並沒有simpleOperation2()方法,而客戶端則期待這個方法。爲使客戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的實例,從而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關係,這決定了這個適配器模式是對象的。
從上圖可以看出,模式所涉及的角色有:
目標(Target)角色:這就是期待的接口,目標可以是具體的或抽象的類。
源(Adaptee)角色:現有需要適配的接口。
適配器(Adapter)角色:適配器類是本模式的核心。適配器把源接口轉換成目標接口,顯然,這一角色必須是具體類。

代碼示例:

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

    /**
     * 這是源類沒有的方法simpleOperation2
     */
    void 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
     * 因此適配器類補充上這個方法
     */
    @Override
    public void simpleOperation2() {
        //write you code here
    }
}

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

三.JDK中適配器模式的應用
集合包中java.util.Arrays#asList()、io包中java.io.InputStreamReader(InputStream)、java.io.OutputStreamWriter(OutputStream) 等。
比方說InputStreamReader,創建InputStreamReader對象的時候必須在構造函數中傳入一個InputStream實例,然後InputStreamReader的作用就是將InputStream適配到Reader。很顯然,適配器就是InputStreamReader,源角色就是InputStream代表的實例對象,目標接口就是Reader類。
OutputStreamWriter也是類似的方式。

四.總結
適配器模式將現有接口轉化爲客戶類所期望的接口,實現了對現有類的複用,它是一種使用頻率非常高的設計模式,在軟件開發中得以廣泛應用,在Spring等開源框架、驅動程序設計(如JDBC中的數據庫驅動程序)中也使用了適配器模式。
主要優點
無論是對象適配器模式還是類適配器模式都具有如下優點:
將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,無須修改原有結構。
增加了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,而且提高了適配者的複用性,同一個適配者類可以在多個不同的系統中複用。
靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”。
具體來說,類適配器模式還有如下優點:
由於適配器類是適配者類的子類,因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。
對象適配器模式還有如下優點:
一個對象適配器可以把多個不同的適配者適配到同一個目標;
可以適配一個適配者的子類,由於適配器和適配者之間是關聯關係,根據“里氏代換原則”,適配者的子類也可通過該適配器進行適配。

主要缺點
類適配器模式的缺點如下:
對於Java等不支持多重類繼承的語言,一次最多隻能適配一個適配者類,不能同時適配多個適配者;
適配者類不能爲最終類,如在Java中不能爲final類;
在Java語言中,類適配器模式中的目標抽象類只能爲接口,不能爲類,其使用有一定的侷限性。
對象適配器模式的缺點如下:
與類適配器模式相比,要在適配器中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然後再把適配者類的子類當做真正的適配者進行適配,實現過程較爲複雜。

適用場景
在以下情況下可以考慮使用適配器模式:
系統需要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需要,甚至沒有這些類的源代碼。
想創建一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。
(對對象的適配器模式而言)在設計裏,需要改變多個已有的子類的接口,如果使用類的適配器模式,就要針對每一個子類做一個適配器類,而這不太實際。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章