設計模式(七)介面卡模式

thekingisalwayslucky發表於2019-04-22

介面卡模式是將一個類的介面轉換成客戶希望的另外一個介面,身邊很多東西都是適用於介面卡模式的,筆記本的電源(也叫電源介面卡),是將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() {
        com.demo.adapter.objadapter.PowerAdapter adapter = new com.demo.adapter.objadapter.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介面。

note:我以為努力就會有回報的

相關文章