適配器模式

2018年10月17日08:50:11java

適配器模式(adapter pattern)

定義

我喜歡的樣子你都有數據庫

你喜歡的樣子我有沒有設計模式

沒有的話,我送你個適配器,好嗎ide

將一個類的接口轉換成客戶但願的另一個接口。適配器模式使得本來因爲接口不兼容而不能在一塊兒工做的那些類能夠一塊兒工做。——《設計模式:可複用面向對象軟件的基礎》工具

適配器模式是一種對象結構型模式。測試

這裏的接口不只僅是java語言中的interface,更可能是指一個類型所具備的方法特徵集合,是一種邏輯上的抽象。this

使用場景

客戶端須要一個target(目標)接口,可是不能直接重用已經存在的adaptee(適配者)類,由於它的接口和target接口不一致,因此須要adapter(適配器)將adaptee轉換爲target接口。前提是target接口和已存在的適配者adaptee類所作的事情是相同或類似,只是接口不一樣且都不易修改。若是在設計之初,最好不要考慮這種設計模式。凡事都有例外,就是設計新系統的時候考慮使用第三方組件,由於咱們就不必爲了迎合它修改本身的設計風格,能夠嘗試使用適配器模式。設計

這是一個適配器使用場景的例子:日誌

Sun公司在1996年公開了Java語言的數據庫鏈接工具JDBC,JDBC使得Java語言程序可以與數據庫鏈接,並使用SQL語言來查詢和操做數據。JDBC給出一個客戶端通用的抽象接口,每個具體數據庫引擎(如SQL Server、Oracle、MySQL等)的JDBC驅動軟件都是一個介於JDBC接口和數據庫引擎接口之間的適配器軟件。抽象的JDBC接口和各個數據庫引擎API之間都須要相應的適配器軟件,這就是爲各個不一樣數據庫引擎準備的驅動程序。code

角色

目標角色(target):這是客戶鎖期待的接口。目標能夠是具體的或抽象的類,也能夠是接口

適配者角色(adaptee):已有接口,可是和客戶器期待的接口不兼容。

適配器角色(adapter):將已有接口轉換成目標接口。

分類

適配器模式有分三類:

  • 一、類適配器模式(class adapter pattern)

  • 二、對象適配器模式(object adapter pattern)

  • 三、缺省適配器模式(default adapter pattern),也叫默認適配器模式、接口適配器模式

類適配器模式(class adapter pattern)

類適配器模式在編譯時實現target(目標)接口。這種適配器模式使用了多個實現了期待的接口或者已經存在的接口的多態接口。比較典型的就是:target接口被建立爲一個純粹的接口,如Java不支持多繼承的語言。

圖示

類適配器模式(adapter pattern)結構圖:
類適配器模式

如上圖,由於java沒有類多繼承,因此只能實現Target接口,並且Target只能是接口。Adapter實現了Target接口,繼承了Adaptee類,Target.operation()實現爲Adaptee.specificOperation()。

客戶端調用類適配器:
客戶端調用類適配器

這個圖是Adapter適配器多繼承的狀況,引用維基百科,能夠看到客戶端調用適配器Adapter的methodA時候,實際上調用的是Adapter繼承過來的method1到methodN。

代碼示例

一張圖說明需求:

電源適配器

嗯,就是電源適配器了。上面有這樣兩行:

輸入:100-240V ~ 0.5A 50-60HZ

輸出:5.2V ==== 2.4A

咱們的需求就是將電源輸入220V(適配者)轉換爲5V輸出(目標)。

目標角色(PowerTarget.java):

public interface PowerTarget {
    public int output5V();
}

電源目標。

適配者角色(PowerAdaptee.java):

public class PowerAdaptee {
    private int output =  220;
    public int output220V() {
        System.out.println("電源輸出電壓:" + output);
        return output;
    }
}

電源適配者。

適配器角色(PowerAdapter.java):

public class PowerAdapter extends PowerAdaptee implements PowerTarget{
    
    @Override
    public int output5V() {
        int output = output220V();
        System.out.println("電源適配器開始工做,此時輸出電壓是:" + output);
        output = output/44;
        System.out.println("電源適配器工做完成,此時輸出電壓是:" + output);
        return output;
    }
    
}

電源適配器類實現了電源目標,繼承了適配者。其實若是沒有我打印的那些提示或者說日誌,output5V方法能夠直接寫成:

public int output5V() {
        return output220V()/44;
    }

這樣就適配了。

類適配器模式測試類(ClassAdapterPatternTest.java):

public class ClassAdapterPatternTest {
    
    public static void main(String[] args) {
        PowerTarget target = new PowerAdapter();
        target.output5V();
    }
}

測試結果:
適配器模式示例測試結果

對象適配器模式(object adapter pattern)

對象適配器模式在運行時實現target(目標)接口。在這種適配器模式中,適配器包裝了一個類實例。在這種狀況下,適配器調用包裝對象實例的方法。

圖示

對象適配器模式(object adapter pattern)結構圖:
對象適配器模式結構圖

如上圖,與類適配器模式不一樣的是,Adapter只實現了Target的接口,沒有繼承Adaptee,而是使用聚合的方式引用adaptee。

客戶端調用對象適配器:
客戶端調用對象適配器

客戶端調用對象適配器方法methodA的時候,實際上調用的是建立對象傳進來的適配者實例的方法methodB。

代碼示例

代碼示例和類適配器模式只有Adapter類有不一樣,其餘完成同樣,連測試結果都是同樣。下面只貼上Adapter類。

適配器角色(Adapter.java):

public class PowerAdapter implements PowerTarget{
    private PowerAdaptee powerAdaptee;

    public PowerAdapter(PowerAdaptee powerAdaptee) {
        super();
        this.powerAdaptee = powerAdaptee;
    }

    @Override
    public int output5V() {
        int output = powerAdaptee.output220V();
        System.out.println("電源適配器開始工做,此時輸出電壓是:" + output);
        output = output/44;
        System.out.println("電源適配器工做完成,此時輸出電壓是:" + output);
        return output;
    }
    
}

實現了PowerTarget(目標角色),在建立對象時引入PowerAdaptee(適配者角色)。

類適配器模式和對象適配器模式的對比

優勢

類適配器模式(class adapter pattern):

因爲適配器adapter類是適配者adaptee類的子類,所以能夠在適配器類中置換一些適配者的方法,即Override(重寫),使得適配器的靈活性更強。

對象適配器模式(object adapter pattern):

一個對象適配器能夠把多個不一樣的適配者adaptee適配到一個目標,也就是說,同一個適配器能夠將適配者類和它的子類都適配到目標接口。

缺點

類適配器模式:

java8以前:接口沒有default方法,就是沒有實現了具體邏輯的方法,並且不支持類多繼承,因此適配者類只能有一個

java8以後:接口有了default方法,接口中的方法有了實現,由於接口是多繼承的,因此適配者能夠是多個帶有default方法的接口,可是接口是不能夠實例化的,實際上沒有什麼意義。有個解決方法就是,接口裏都是default方法,實現接口的類什麼也沒作,就是提供一個能夠實例化的類。這樣的化,類適配器模式中適配者adapter類就能夠適配多個適配者adaptee類了。這個解決方法只是我理論上論證一下,實際上可行與否,請自行判斷驗證。

對象適配器模式:

類適配器模式的優勢就是對象適配器模式的缺點,不能置換適配者類的方法。若是想修改適配者類的一個或多個方法,就只好先建立一個繼承與適配者類的子類,把適配者類的方法置換掉,而後把適配者的子類當作真正的適配者進行適配,實現過程較爲複雜。

缺省適配器模式(default adapter pattern)

當不須要所有實現接口提供的方法時,能夠設計一個適配器抽象類實現接口,併爲接口中的每一個方法提供默認方法,抽象類的子類就能夠有選擇的覆蓋父類的某些方法實現需求,它適用於一個接口不想使用全部的方法的狀況。在java8後,接口中能夠有default方法,就不須要這種缺省適配器模式了。接口中方法都設置爲default,實現爲空,這樣一樣一樣能夠達到缺省適配器模式一樣的效果。

圖示

缺省適配器模式結構圖:
缺省適配器模式結構圖

適配器Adapter類實現Target接口,方法默認爲空。

代碼示例

目標角色(SampleOperation.java):

public interface SampleOperation {
    public abstract void operation1();
    public abstract void operation2();
    public abstract void operation3();
    public abstract void operation4();
    public abstract void operation5();
}

包含了不少操做。

適配器角色(DefaultAdapter.java):

public abstract class DefaultAdapter implements SampleOperation{

    @Override
    public void operation1() {
    }

    @Override
    public void operation2() {
    }

    @Override
    public void operation3() {
    }

    @Override
    public void operation4() {
    }

    @Override
    public void operation5() {
    }
}

默認實現了全部操做

這個是測試缺省適配器模式須要用到的類(Operator.java):

public class Operator {
    private SampleOperation sampleOperation;
    
    public void addOperation(SampleOperation sampleOperation) {
        this.sampleOperation = sampleOperation;
    }

    public void operation1() {
        sampleOperation.operation1();
    }

    public void operation2() {
        sampleOperation.operation2();
    }

    public void operation3() {
        sampleOperation.operation3();
    }

    public void operation4() {
        sampleOperation.operation4();
    }

    public void operation5() {
        sampleOperation.operation5();
    }
}

缺省適配器模式測試類(DefaultAdapterTest.java):

public class DefaultAdapterTest {

    public static void main(String[] args) {
        
        // 一、原來要實現全部操做類的操做
        Operator operator1 = new Operator();
        operator1.addOperation(new SampleOperation() {

            @Override
            public void operation1() {}

            @Override
            public void operation2() {
                System.out.println("操做2");
            }

            @Override
            public void operation3() {}

            @Override
            public void operation4() {}

            @Override
            public void operation5() {}
            
        });
        operator1.operation2();
        
        // 二、使用缺省適配器只須要實現須要用到的接口方法
        Operator operator2 = new Operator();
        operator2.addOperation(new DefaultAdapter() {
            
            @Override
            public void operation2() {
                System.out.println("操做2");
            }
        });
        operator2.operation2();
    }
}

測試類須要執行操做2,operator1添加SampleOperation時要實現接口裏全部方法,operator2添加SampleOperation時只須要經過DefaultAdapter適配器添加本身須要的操做便可。毫無疑問,測試結果是同樣的。
缺省適配器模式測試結果

優勢

一、複用性:系統須要使用已經存在的類,功能符合系統要求,但這個類的接口不符合系統的需求,經過適配器模式解決不兼容的問題,使這些功能類獲得複用。

二、擴展性:適配器使得系統多了一個方式擴展系統的功能

三、耦合性:必定程度上的解耦

缺點

過多地使用適配器,增長系統理解難度。

總結

本文主要介紹了三種適配器模式,本質上是現有的不兼容的接口轉換爲須要的接口。

類適配器模式,以繼承現有類的方式轉換。

對象適配器模式,以聚合對象實例的方式轉換。

接口適配器模式,以實現接口的方式轉換。

適配器模式是在現有的類和系統都不易修改的狀況下使用,在系統設計之初慎用適配器模式。

2018年10月18日14:53:33

相關文章
相關標籤/搜索