Java設計模式之(八)——適配器模式

1、什麼是適配器模式?

Convert the interface of a class into another interface clients expect.Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.

適配器模式(Adapter Pattern):將一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作。

說人話:這個模式就是用來做適配的,它將不兼容的介面轉換為可兼容的介面,讓原本由於介面不兼容而不能一起工作的類可以一起工作。比如現實生活中的例子, USB 轉接頭就充當適配器,把兩種不兼容的介面,通過轉接變得可以一起工作。

2、適配器模式定義

image-20210910001149584

①、Target目標角色

該角色定義把其他類轉換為何種介面, 也就是我們的期望介面, 例子中的IUserInfo介面就是目標角色。

②、Adaptee源角色

你想把誰轉換成目標角色, 這個「誰」就是源角色, 它是已經存在的、 運行良好的類或對象, 經過適配器角色的包裝, 它會成為一個嶄新、 靚麗的角色。

③、Adapter適配器角色

適配器模式的核心角色, 其他兩個角色都是已經存在的角色, 而適配器角色是需要新建立的, 它的職責非常簡單: 把源角色轉換為目標角色, 怎麼轉換? 通過繼承或是類關聯的方式。

3、適配器模式通用程式碼實現

/**
 * 目標角色
 */
public interface Target {
    void t1();
    void t2();
    void t3();
}
/**
 * 目標角色實現類
 */
public class ConcreteTarget implements Target{

    @Override
    public void t1() {
        System.out.println("目標角色 t1 方法");
    }

    @Override
    public void t2() {
        System.out.println("目標角色 t2 方法");
    }

    @Override
    public void t3() {
        System.out.println("目標角色 t3 方法");
    }
}
/**
 * 源角色:要把源角色轉換成目標角色
 */
public class Adaptee {

    public void a1(){
        System.out.println("源角色 a1 方法");
    }

    public void a2(){
        System.out.println("源角色 a2 方法");
    }

    public void a3(){
        System.out.println("源角色 a3 方法");
    }
}

基於繼承的類適配器

/**
 * 適配器角色
 */
public class Adapter extends Adaptee implements Target{

    @Override
    public void t1() {
        super.a1();
    }

    @Override
    public void t2() {
        super.a2();
    }

    @Override
    public void t3() {
        super.a3();
    }
}

基於組合的對象適配器

public class AdapterCompose implements Target{

    private Adaptee adaptee;

    public AdapterCompose(Adaptee adaptee){
        this.adaptee = adaptee;
    }
    @Override
    public void t1() {
        adaptee.a1();
    }

    @Override
    public void t2() {
        adaptee.a2();
    }

    @Override
    public void t3() {
        adaptee.a3();
    }
}

測試:

public class AdapterClient {

    public static void main(String[] args) {
        // 原有的業務邏輯
        Target target = new ConcreteTarget();
        target.t1();

        // 基於繼承 增加適配器業務邏輯
        Target target1 = new Adapter();
        target1.t1();

        // 基於組合 增加適配器業務邏輯
        Target target2 = new AdapterCompose(new Adaptee());
        target2.t1();
    }
}

列印結果:

image-20210910084111245

適配器模式有兩種實現方式:類適配器和對象適配器。其中,類適配器使用繼承關係來實現,對象適配器使用組合關係來實現。在實際開發中,選擇的依據如下:

①、如果 Adaptee 介面並不多,那兩種實現方式都可以。

②、如果 Adaptee 介面很多,而且 Adaptee 和 ITarget 介面定義大部分都相同,那我們推薦使用類適配器,因為 Adaptor 復用父類 Adaptee 的介面,比起對象適配器的實現方式,Adaptor 的程式碼量要少一些。

③、如果 Adaptee 介面很多,而且 Adaptee 和 ITarget 介面定義大部分都不相同,那我們推薦使用對象適配器,因為組合結構相對於繼承更加靈活。

4、適配器模式優點

①、適配器模式可以讓兩個沒有任何關係的類在一起運行, 只要適配器這個角色能夠搞定他們就成。

②、增加了類的透明性

我們訪問的Target目標角色, 但是具體的實現都委託給了源角色, 而這些對高層次模組是透明的, 也是它不需要關心的。

③、提高了類的復用度

源角色在原有的系統中還是可以正常使用, 而在目標角色中也可以充當新的演員。

④、靈活性非常好

適配器可以隨時去掉,而不會影響很多程式碼。

5、適配器模式應用場景

①、修改已使用的介面

某個已經投產中的介面需要修改,這時候使用適配器最好。

②、統一多個類的介面設計

比如對於敏感詞過濾,需要調用好幾個第三方介面,每個介面方法名,方法參數又不一樣,這時候使用適配器模式,將所有第三方的介面適配為統一的介面定義。

③、兼容老版本介面

比如JDK1.0 中包含一個遍歷集合容器的類 Enumeration,JDK2.0 對這個類進行了重構,將它改名為 Iterator 類,並且對它的程式碼實現做了優化。但是考慮到如果將 Enumeration 直接從 JDK2.0 中刪除,那使用 JDK1.0 的項目如果切換到 JDK2.0,程式碼就會編譯不通過。為了避免這種情況的發生,我們必須把項目中所有使用到 Enumeration 的地方,都修改為使用 Iterator 才行。

單獨一個項目做 Enumeration 到 Iterator 的替換,勉強還能接受。但是,使用 Java 開發的項目太多了,一次 JDK 的升級,導致所有的項目不做程式碼修改就會編譯報錯,這顯然是不合理的。這就是我們經常所說的不兼容升級。為了做到兼容使用低版本 JDK 的老程式碼,我們可以暫時保留 Enumeration 類,並將其實現替換為直接調用 Itertor。

public class Collections {
  public static Emueration emumeration(final Collection c) {
    return new Enumeration() {
      Iterator i = c.iterator();
      
      public boolean hasMoreElments() {
        return i.hashNext();
      }
      
      public Object nextElement() {
        return i.next():
      }
    }
  }
}

④、適配不同格式的數據

6、代理-橋接-裝飾器-適配器區別

①、代理模式在不改變原始類介面的條件下,為原始類定義一個代理類,主要目的是控制訪問,而非加強功能,這是它跟裝飾器模式最大的不同。

②、橋接模式:橋接模式的目的是將介面部分和實現部分分離,從而讓它們可以較為容易、也相對獨立地加以改變。

③、裝飾器模式:裝飾者模式在不改變原始類介面的情況下,對原始類功能進行增強,並且支援多個裝飾器的嵌套使用。

④、適配器模式:適配器模式是一種事後的補救策略。適配器提供跟原始類不同的介面,而代理模式、裝飾器模式提供的都是跟原始類相同的介面。