適配器模式一篇就夠了

很常用的結構型設計模式,用做兩個不兼容的接口之間的橋樑,結合了兩個獨立接口的功能。

經常看到有人濫用,並且會和策略模式、橋接模式混淆,這裏主要結合例子說明下,如何恰當的引入的適配器模式。

使用場景

一、需求

中國標準電壓是 220V,電器也是支持 220V的,代碼實現使用電器的邏輯。
創建電器類:

public class ChinaDevice {

    /**
     * 使用電器
     *
     * @param voltage 輸入電壓
     */
    public void use(int voltage) {
        System.out.println("輸入電壓" + voltage + "V");
        if (voltage == 220) {
            System.out.println("中國電器正常運行");
        } else {
            System.out.println("中國電器燒燬");
        }
        System.out.println();
    }
}

創建電源類:

public class ChinaPower {
    public void run(ChinaDevice device) {
        // 使用220V電壓運行
        device.use(220);
    }
}

使用電器:

public class DeviceUnitTest {

    @Test
    public void test() {
        // 創建220V電源
        ChinaPower chinaPower = new ChinaPower();
        // 創建電器
        ChinaDevice device = new ChinaDevice();
        // 運行電器
        chinaPower.run(device);
    }
}

開發完成,電器正常運行起來了。

輸入電壓220V
中國電器正常運行

二、拓展需求

產品經理拿來一個美國電器,僅支持 110V 電壓,要求也要正常運行起來。

考慮到產品需求一天一遍,每個國家電器標準都不一樣,這次抽離一個接口:

public interface Device {

    /**
     * 使用電器
     *
     * @param voltage 輸入電壓
     */
    void use(int voltage);
}

創建美國電器類:

public class USADevice implements Device {

    @Override
    public void use(int voltage) {
        System.out.println("輸入電壓" + voltage + "V");
        if (voltage == 110) {
            System.out.println("美國電器正常運行");
        } else {
            System.out.println("美國電器燒燬");
        }
        System.out.println();
    }
}

直接使用 220V 必然燒燬,那麼應該如何解決呢?

很簡單,創建一個美國電源類就可以了:

public class USAPower {
    public void run(Device device) {
        device.use(110);
    }
}

這種方式當然沒有問題,但是隨着國家的增多,電源類也會越來越多。

從實際生活的角度看,我們也不可能擁有多個電壓的電源,只能藉助變壓器(適配器)來解決該問題。

三、引入適配器模式

下面來創建一個 220V 的適配器:

public class ChinaAdapter implements Device {

    private USADevice mUsaDevice;

    /**
     * 默認中國電器
     */
    public ChinaAdapter() {

    }

    /**
     * 適配美國電器
     */
    public ChinaAdapter(USADevice usaDevice) {
        this.mUsaDevice = usaDevice;
    }

    @Override
    public void use(int voltage) {
        System.out.println("輸入電壓" + voltage + "V");
        if (mUsaDevice != null) {// 適配美國電器
            if (voltage == 110) {
                System.out.println("美國電器正常運行");
            } else {
                System.out.println("適配器工作...進行變壓");
                voltage = 110;
                System.out.println("美國電器在" + voltage + "V正常運行");
            }
        } else {// 默認中國電器
            if (voltage == 220) {
                System.out.println("中國電器正常運行");
            } else {
                System.out.println("中國電器燒燬");
            }
        }
        System.out.println();
    }
}

仔細對比 ChinaAdapterChinaDevice,可以發現,這個適配器只是擴展了 ChinaDevice,使其支持 USADevice 美國電器,本質上還是中國電器。

  • 無參構造 創建的是中國電器,在 use 方法中,僅支持 220V。
  • 帶參構造 創建的是美國電器,在 use 方法中,將 220V 轉爲 110V。

使用適配器:

public class DeviceUnitTest {

    @Test
    public void test() {
        // 創建220V電源
        ChinaPower chinaPower = new ChinaPower();
        // 適配器
        Device device = new ChinaAdapter(new USADevice());
        chinaPower.run(device);
        // 默認構造,就是支持220V的中國電器
        device = new ChinaAdapter();
        chinaPower.run(device);
    }
}

四、優勢

在這種模式下,220V 電源不需要知道是那種電器,只要負責供電即可,具體操作的什麼類,由適配器決定。

適配器模式具有以下優勢:

  1. 可以讓任何兩個沒有關聯的類一起運行。
  2. 提高了類的複用。
  3. 靈活性好。

總結

  • 意圖:將一個類的接口轉換成另外一個接口。適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以一起工作。
  • 主要解決:常常要將一些"現存的對象"放到新的環境中,而新環境要求的接口是現對象不能滿足的。
  • 如何解決:繼承類或實現接口(推薦)。
  • 關鍵代碼:適配器繼承已有的對象,實現想要的目標接口。
  • 缺點:過多地使用適配器,會使代碼非常凌亂。比如,明明看到調用的是 A 接口,其實內部被適配成了 B 接口的實現,如果太多出現這種情況,無異於一場災難。因此如果不是很有必要,可以不使用適配器,而是直接進行重構
  • 注意事項:適配器不是在詳細設計時添加的,而是解決正在服役的項目的問題。

參考

適配器模式
適配器和策略模式的聯繫與區別

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