設計模式之介面卡模式

Ericyshi發表於2018-07-21

介面卡模式

一:介面卡模式概述

​ 介面卡其實大家都知道,比如電源介面卡,筆記本電壓20多V,而家庭用電220V,所以我們必須解決讓20v左右的筆記本在220V環境下工作,那麼便需要電源介面卡了

​ 與電源介面卡相似,在介面卡模式中引入了一個被稱為介面卡(Adapter)的包裝類,而它所包裝的物件稱為適配者(Adaptee),即被適配的類。介面卡的實現就是把客戶類的請求轉化為對適配者的相應介面的呼叫。也就是說:當客戶類呼叫介面卡的方法時,在介面卡類的內部將呼叫適配者類的方法,而這個過程對客戶類是透明的,客戶類並不直接訪問適配者類。因此,介面卡讓那些由於介面不相容而不能互動的類可以一起工作。

​ 介面卡模式可以將一個類的介面和另一個類的介面匹配起來,而無須修改原來的適配者介面和抽象目標類介面。介面卡模式定義如下:

將一個介面轉換成客戶希望的另一個介面,使介面不相容的那些類可以一起工作,其別名為包裝器(Wrapper)。介面卡模式既可以作為類結構型模式,也可以作為物件結構型模式。

​ 在介面卡模式中,我們通過增加一個新的介面卡類來解決介面不相容的問題,使得原本沒有任何關係的類可以協同工作。根據介面卡類與適配者類的關係不同,介面卡模式可分為物件介面卡類介面卡兩種,在物件介面卡模式中,介面卡與適配者之間是關聯關係;在類介面卡模式中,介面卡與適配者之間是繼承(或實現)關係。在實際開發中,物件介面卡的使用頻率更高。


二:物件介面卡結構圖

1545722731275

角色說明:

  • Target(目標抽象類):目標抽象類定義客戶所需介面,可以是一個抽象類或介面,也可以是具體類。
  • Adapter(介面卡類):介面卡可以呼叫另一個介面,作為一個轉換器,對Adaptee和Target進行適配,介面卡類是介面卡模式的核心,在物件介面卡中,它通過繼承Target並關聯一個Adaptee物件使二者產生聯絡。
  • Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的介面,這個介面需要適配,適配者類一般是一個具體類,包含了客戶希望使用的業務方法,在某些情況下可能沒有適配者類的原始碼。

根據物件介面卡模式結構圖,在物件介面卡中,客戶端需要呼叫request()方法,而適配者類Adaptee沒有該方法,但是它所提供的specificRequest()方法卻是客戶端所需要的。為了使客戶端能夠使用適配者類,需要提供一個包裝類Adapter,即介面卡類。這個包裝類包裝了一個適配者的例項,從而將客戶端與適配者銜接起來,在介面卡的request()方法中呼叫適配者的specificRequest()方法。因為介面卡類與適配者類是關聯關係(也可稱之為委派關係),所以這種介面卡模式稱為物件介面卡模式。客戶端只看到目標介面而不是介面卡。介面卡實現目標介面。介面卡將所有請求委託給Adaptee

三:案例分析

假設你有一個帶有fly()和makeSound()<發出聲音類>方法的Bird類。還有一個帶有squeak()<吱吱叫類>方法的ToyDuck類。假設鳥是咕咕叫,鴨子是吱吱叫,實現了不同的介面,現在我希望鴨子發出鳥叫聲。所以我們將使用介面卡模式。在這裡,我們的客戶將是ToyDuck,而adaptee將是Bird。

1545723558692

如圖:BirdAdapter將實現ToyDuck介面,類中將關聯一個Bird類,介面,當呼叫Squea()介面時,介面卡將呼叫Bird介面的makeSound方法,從而實現鴨子發出鳥叫聲的功能。

角色分析:

Bird:

// 鳥類實現介面允許飛行和發出 聲音
public interface Bird {
    public void fly();
    public void makeSound();
}
複製程式碼

Sparrow:

// 麻雀
public class Sparrow implements Bird {
    @Override
    public void fly() {
        System.out.println("正在飛");
    }
    @Override
    public void makeSound() {
        System.out.println("咕咕叫");
    }
}
複製程式碼

ToyDuck:

// 玩具鴨要實現的介面,不會飛,只會吱吱叫
public interface ToyDuck {
    // 吱吱吱叫
    public void squeak();
}
複製程式碼

PlasticToyDuck:

// 塑料玩具鴨
public class PlasticToyDuck implements ToyDuck{
    @Override
    public void squeak() {
        System.out.println("吱吱叫");
    }
}
複製程式碼

BirdAdapter:

public class BirdAdapter implements ToyDuck {
    // 需要實現客戶端希望使用的介面
    Bird bird;
    public BirdAdapter(Bird bird)
    {
        // 需要引用正在使用的物件
        this.bird = bird;
    }

    @Override
    public void squeak() {
        bird.makeSound();
    }
}
複製程式碼

client:

public class Client {
    public static void main(String[] args) {
        // 麻雀(適配者 Adaptee,被適配的類)
        Bird sparrow = new Sparrow();
        // 介面卡(adapter)
        ToyDuck birdAdapter = new BirdAdapter(sparrow);

        // 玩具鴨表現的像一隻鳥
        System.out.println("我是鴨子,但我發出鳥叫聲:");
        birdAdapter.squeak();
    }
}
複製程式碼

我是鴨子,但我發出鳥叫聲: 咕咕叫

四:類介面卡結構圖

1545724369779

這是類介面卡結構圖,adapter需要繼承自目標物件並實現介面,而很多語言都不支援多重繼承,所以這種模式很少使用。


五:介面卡模式總結

好處:

  • 將目標類和適配者類解耦,通過引入一個介面卡類來重用現有的適配者類,無須修改原有結構。。
  • 客戶端類並不複雜,必須使用不同的介面,並且可以使用多型在不同的介面卡實現之間進行交換。
  • 增加了類的透明性和複用性,將具體的業務實現過程封裝在適配者類中,對於客戶端類而言是透明的,而且提高了適配者的複用性,同一個適配者類可以在多個不同的系統中複用
  • 靈活性和擴充套件性都非常好,通過使用配置檔案,可以很方便地更換介面卡,也可以在不修改原有程式碼的基礎上增加新的介面卡類,完全符合“開閉原則”。

物件介面卡優勢:

  • 一個物件介面卡可以把多個不同的適配者適配到同一個目標
  • 可以適配一個適配者的子類,由於介面卡和適配者之間是關聯關係,根據“里氏代換原則”,適配者的子類也可通過該介面卡進行適配。

物件介面卡的劣勢:

  • 要在介面卡中置換適配者類的某些方法比較麻煩。如果一定要置換掉適配者類的一個或多個方法,可以先做一個適配者類的子類,將適配者類的方法置換掉,然後再把適配者類的子類當做真正的適配者進行適配,實現過程較為複雜。

缺點:

  • 所有請求都被轉發,因此開銷略有增加。
  • 有時需要在介面卡鏈上進行許多調整以達到所需的型別。

使用場景:

  • 系統需要使用一些現有的類,而這些類的介面(如方法名)不符合系統的需要,甚至沒有這些類的原始碼
  • 想建立一個可以重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作。

相關文章