介面卡模式:我真的不難

TimberLiu發表於2019-05-13

前面三篇文章分別學習了單例模式、三種工廠模式和建造者模式,它們都是比較常用的建立型模式,顧名思義就是建立物件的。從這篇文章開始來學習結構型設計模式,今天是第一篇——介面卡模式。

介面卡模式

首先拿我使用的小米手機為例,它撤銷了原來的 Audio 介面,要使用耳機聽歌呢,就必須使用 Type-C to Audio 轉接線(如下圖),一頭接上手機,一頭接上耳機線。(ps:話說,每次聽歌和充電都要換來換去的,好麻煩)

介面卡模式:我真的不難

其實,這就是一個實際的介面卡,和設計模式中的介面卡扮演著同樣的角色,將一個介面轉換為另一個介面,以符合使用者的期望。

改進前

下面我們就將這個例子轉換為程式碼,看它是如何實現的。以前的手機呢,有兩個介面,使用 TypeC 介面充電:

public interface TypeCInterface {
    // 充電
    void chargeWithTypeC();
}

public class TypeCInterfaceImpl implements TypeCInterface {

    @Override
    public void chargeWithTypeC() {
        System.out.println("使用 Type-C 介面充電");
    }
}
複製程式碼

使用 Audio 介面聽歌:

public interface AudioInterface {
    // 聽歌
    void listenWithAudio();
}

public class AudioInterfaceImpl implements AudioInterface {

    @Override
    public void listenWithAudio() {
        System.out.println("使用 Audio 介面聽歌");
    }
}
複製程式碼

而我們的手機,同時有這兩個介面,用來充電和聽歌:

public class XiaomiPhone {

    private AudioInterface audioInterface;
    private TypeCInterface typeCInterface;
    
    public XiaomiPhone(AudioInterface audioInterface, TypeCInterface typeCInterface) {
        this.audioInterface = audioInterface;
        this.typeCInterface = typeCInterface;
    }
    
    public void charge() {
        typeCInterface.chargeWithTypeC();
    }
    
    public void listen() {
        audioInterface.listenWithAudio();
    }
}
複製程式碼

而我們就可以用手機來邊充電,邊聽歌了:

public class Client {

    public static void main(String[] args) {
        AudioInterface audioInterface = new AudioInterfaceImpl();
        TypeCInterface typeCInterface = new TypeCInterfaceImpl();
        
        XiaomiPhone xiaomiPhone = new XiaomiPhone(audioInterface, typeCInterface);
        xiaomiPhone.charge();
        xiaomiPhone.listen();
    }
}
複製程式碼

改進了

本來這一切都好好的,可是小米手機把 Audio 介面取消了,我們沒法直接使用來聽歌了。於是,我們只好使用轉接線,將 Type-C 介面轉為 Audio 介面:

// 需將其轉換為 Audio 介面,所以實現了 AudioInterface
public class TypeCToAudioTieline implements AudioInterface {

    private TypeCInterface typeCInterface;

    // 另一頭是 TypeC,所以傳入 TypeCInterface
    public TypeCToAudioTieline(TypeCInterface typeCInterface) {
        this.typeCInterface = typeCInterface;
    }

    @Override
    public void listenWithAudio() {
        // ···
        typeCInterface.chargeWithTypeC();
    }
}
複製程式碼

然後呢,把轉接線插入到手機上(把手機和轉接線看作一個整體,它只有 Audio 介面了):

public class XiaomiPhone {

    private AudioInterface audioInterface;

    public XiaomiPhone(AudioInterface audioInterface) {
        this.audioInterface = audioInterface;
    }

    public void listenWithAudio() {
        audioInterface.listenWithAudio();
    }
}
複製程式碼

於是,現在我們就通過轉接線,將 Type-C 介面轉換成了 Audio 介面。然後將耳機插在轉接線上,就可以聽歌了:

public class Client {

    public static void main(String[] args) {
        TypeCInterface typeCInterface = new TypeCInterfaceImpl();
        TypeCToAudioTieline tieline = new TypeCToAudioTieline(typeCInterface);

        XiaomiPhone xiaomiPhone = new XiaomiPhone(tieline);
        xiaomiPhone.listenWithAudio();
    }
}
複製程式碼

上述模式就是介面卡模式,它將一個類的介面轉換成使用者所需要的另一個介面,使得原本介面不相容的類可以一起工作。

它的 UML 圖如下:

介面卡模式:我真的不難

下面我們來總結介面卡模式的優點:

  • 它可以通過介面卡進行介面的轉換,讓原本不相容的類協同工作;
  • 這可以使客戶從實現的介面解耦,如果被適配者改變了介面,介面卡可以將其封裝起來,客戶不必跟隨其修改;

缺點:

  • 增加一個介面卡,可能會增加系統的複雜度。

介面卡模式的具體實踐

JDK#InputStreamReader

通過 InputStreamReader,可以將 InputStream 位元組流轉換為字元流進行處理。

public class InputStreamReader extends Reader {

    private final StreamDecoder sd;
    
    // 將 inputStream 轉換為 InputStreamReader
    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }
    
    public int read() throws IOException {
        return sd.read();
    }
}
複製程式碼

Spring#AOP/MVC

另外,比如在 Spring AOP 中,將 Advice 封裝成對應的攔截器型別。或是在 Spring MVC 中,通過介面卡模式,用於執行目標 Controller 中的請求處理方法。

由於對其原始碼不太熟悉,這裡也就不詳細說了。感興趣的小夥伴可以看看這篇文章。

相關文章