適配器模式

前言

適配器模式是將一個類的接口轉換成客戶希望的另外一個接口,身邊很多東西都是適用於適配器模式的,筆記本的電源(也叫電源適配器),是將220V的交流電轉換爲筆記本電腦所需要的12V(電流先忽略),筆記本電腦的各種接口,VGA轉Hdml,USB-TypeA 轉 USB-TypeC,亦或者你在香港買了個手機,充電器是你生活中沒見過的三孔插座通過一個轉換頭轉換爲國內常用的插頭,很多例子都能很形象的解釋這個設計模式。適配器模式(有時候也稱包裝樣式或者包裝)將一個類的接口適配成用戶所期待的。一個適配允許通常因爲接口不兼容而不能在一起工作的類工作在一起,做法是將類自己的接口包裹在一個已存在的類中。

UML角色

Source:需要被適配的類、接口、對象,即Datas。
Destination:需要得到的類,Source通過適配得到的類對象,也就是我們期待得到的藉口。
Adapter:適配器類,協調Source和Destination,使兩者能夠協同工作。

適用場景

1,系統需要使用現有的類,但現有的類卻不兼容。
2,需要建立一個可以重複使用的類,用於一些彼此關係不大的類,並易於擴展,以便於面對將來會出現的類。
3,需要一個統一的輸出接口,但是輸入類型卻不可預知。

Demo

簡單的抽象一個場景:手機充電需要將220V的交流電轉化爲手機鋰電池需要的5V直流電,我們的demo就是寫一個電源適配器,將 AC220v ——> DC5V,其實適配器模式可以簡單的分爲三類:類適配器模式、對象的適配器模式、接口的適配器模式。我們就以這三種模式來實現上述步驟。

類適配器模式

就上面提到的功能,簡單的使用類適配器模式,Source類如下:

package com.demo.adapter.classadapter;

/**
 * Created by italkbb on 2018/1/24.
 */

public class AC220 {
    public int output220V(){
        int output = 220;
        return output;
    }
}

我們的目標類Destination,只需要定義方法,由適配器來轉化:

package com.demo.adapter.classadapter;

/**
 * Created by italkbb on 2018/1/24.
 */

public interface DC5 {
    int output5V();
}

Adapter類如下:

package com.demo.adapter.classadapter;

/**
 * Created by italkbb on 2018/1/24.
 */

public class PowerAdapter extends AC220 implements DC5 {
    @Override
    public int output5V() {
        int output = output220V();
        return (output / 44);
    }
}

對於使用,也很簡單:

/**
 * 類適配器使用demo
 */
private void initClassAdapter() {
    DC5 dc5 = new com.demo.adapter.classadapter.PowerAdapter();
    dc5.output5V();
}

因爲java單繼承的緣故,Destination類必須是接口,以便於Adapter去繼承Source並實現Destination,完成適配的功能,但這樣就導致了Adapter裏暴露了Source類的方法,使用起來的成本就增加了。

對象適配器模式

對於同樣的邏輯,我們在以對象適配器模式實現。我們保留AC220和DC5兩個基本類,我們讓Adapter持有Destination類的實例,然後再實現DC5,以這種持有對象的方式來實現適配器功能:

package com.demo.adapter.objadapter;

import com.demo.adapter.classadapter.AC220;
import com.demo.adapter.classadapter.DC5;

/**
 * Created by italkbb on 2018/1/24.
 */

public class PowerAdapter implements DC5{
    private AC220 mAC220;

    public PowerAdapter(AC220 ac220){
        this.mAC220 = ac220;
    }

    @Override
    public int output5V() {
        int output = 0;
        if (mAC220 != null) {
            output = mAC220.output220V() / 44;
        }
        return output;
    }
}

使用代碼:

/**
 * 對象適配器模式demo
 */
private void initObjAdapter() {
   PowerAdapter adapter = new PowerAdapter(new AC220());
    adapter.output5V();
}

對象適配器和類適配器其實算是同一種思想,只不過實現方式不同。再回想裝飾者模式,裝飾者是對Source的裝飾,使用者毫無察覺到Source被裝飾,也就是用法不變。而對於適配器模式用法還是有改變的。

接口適配器模式

對於接口適配器模式,我們就不用擔着眼於220->5,我們的接口可以有更多的抽象方法,這一點在android開發中有很多影子,動畫的適配器有很多接口,但我們只需要關心我們需要的回調方法(詳見AnimatorListenerAdapter類),我們把接口比作萬能適配器:

package com.demo.adapter.interfaceadapter;

/**
 * Created by italkbb on 2018/1/24.
 */

public interface DCOutput {
    int output5V();
    int output9V();
    int output12V();
    int output24V();
}

然後我們要用的是5V的電壓,所以關心5V的適配:

package com.demo.adapter.interfaceadapter;

import com.demo.adapter.classadapter.AC220;

/**
 * Created by italkbb on 2018/1/24.
 */

public class Power5VAdapter extends PowerAdapter {
   public Power5VAdapter(AC220 ac220) {
        super(ac220);
    }

   @Override
    public int output5V() {
        int output = 0;
        if (mAC220 != null) {
            output = mAC220.output220V() / 44;
        }
        return output;
    }
}

但是我們必須存在一箇中間適配器,用於實現默認的接口方法,以至於減少我們適配器的代碼量,讓代碼更加清晰:

package com.demo.adapter.interfaceadapter;

import com.demo.adapter.classadapter.AC220;

/**
 * Created by italkbb on 2018/1/24.
 * 這裏抽象類其實就寫了空方法,等着子類去實現需要的方法。
 */
public abstract class PowerAdapter implements DCOutput{
    protected AC220 mAC220;

    public PowerAdapter(AC220 ac220){
        this.mAC220 = ac220;
    }

    @Override
    public int output5V() {
        return mAC220.output220V();
    }

    @Override
    public int output9V() {
        return mAC220.output220V();
    }

    @Override
    public int output12V() {
        return mAC220.output220V();
    }

    @Override
    public int output24V() {
        return mAC220.output220V();
    }
}

這樣一來我們就只需要重寫父類我們關心的方法了,當然我們有時候可以省略Power5VAdapter類,因爲內部類可以實現我們的方法,就跟使用setOnClickOnLintener(new OnClickOnLintener(){…})一樣,我們來看使用:

   /**
     * 接口適配器模式demo
     */
    private void initinterfaceAdapter() {
        // 已經實現了子類
        com.demo.adapter.interfaceadapter.Power5VAdapter power5VAdapter = new Power5VAdapter(new AC220());
        power5VAdapter.output5V();

   // 直接實現子類
        com.demo.adapter.interfaceadapter.PowerAdapter powerAdapter = new PowerAdapter(new AC220()) {
            @Override
            public int output5V() {
                int output = 0;
                if (mAC220 != null) {
                    output = mAC220.output220V() / 44;
                }
                return output;
            }
        };
        powerAdapter.output5V();
    }

這樣也實現了這個適配功能,而且可以說易於擴展。

總結

可以說Source的存在形式決定了適配器的名字,類適配器就是繼承Source類,對象適配器就是持有Source類,接口適配器就是實現Source接口。

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