設計模式學習筆記(九):適配器模式

1 適配器模式

1.1 定義

將一個接口轉換爲客戶但願的另外一個接口,使接口不兼容的那些類能夠一塊兒工做,別名爲包裝器。
適配器中的接口是廣義的接口,能夠表示一個方法或者方法的集合。java

適配器模式既能夠做爲類結構型模式,也能夠做爲對象結構型模式。ide

1.2 分類

根據適配器與適配者類的關係不一樣,能夠分爲對象適配器模式以及類適配器模式。測試

1.2.1 對象適配器模式

對象適配器模式就是適配器與適配者之間是關聯關係
結構圖以下:
在這裏插入圖片描述spa

1.2.2 類適配器模式

類適配器模式就是適配器與適配者之間是繼承或實現關係。
結構圖以下:
在這裏插入圖片描述
因爲語言特性的限制,好比Java,C#不支持多重繼承,類適配器模式受到不少限制,例如Target若是不是接口而是一個類,就沒法使用類適配器模式。此外若是適配者爲final類也沒法使用適配器模式,在Java等語言中大部分狀況下使用對象適配器模式。設計

1.3 角色

  • Target(目標抽象類):目標抽象類定義客戶所需的接口,能夠是一個抽象類或接口,也能夠是一個具體類
  • Adapter(適配器類):適配器能夠調用另外一個接口,做爲一個轉換器,對Adaptee和Target進行適配。適配器類是適配器模式的核心,在對象適配器模式中,它經過繼承Target並關聯一個Adaptee對象使二者產生聯繫
  • Adaptee(適配者類):適配者即被適配的角色,它定義了一個已經存在的接口,這個接口須要適配,適配者類通常是一個具體類,包含了客戶但願使用的業務方法,在某些狀況下可能沒有適配者類的源代碼

2 實例

2.1 對象適配器

Target類以及實現了Target的類:code

interface Target
{
    void request();
}

class ConcreteTarget implements Target
{
    @Override
    public void request()
    {
        System.out.println("具體Target方法");
    }
}

適配者類:對象

class Adaptee
{
    public void specificRequest()
    {
        System.out.println("Adaptee方法");
    }
}

適配器類(實現了Target,適配者做爲成員變量):blog

class Adapter implements Target
{
    private Adaptee adaptee = new Adaptee();
    @Override
    public void request()
    {
        adaptee.specificRequest();
    }
}

測試:繼承

public class Test
{
    public static void main(String[] args) 
    {
        Target target = new ConcreteTarget();
        target.request();
        Target adapter = new Adapter();
        adapter.request();
    }
}

2.2 類適配器

在上述對象適配器的基礎上,適配者與Target保持不變,適配器繼承了適配者並實現了Target,同時取消了適配者做爲成員變量,在方法內直接調用super.xxx,也就是適配者的方法:接口

class Adapter extends Adaptee implements Target
{
    @Override
    public void request()
    {
        super.specificRequest();
    }
}

2.3 Micro USB與Type-C

假設目前只有一條Micro USB線以及一臺只有Type-C接口的手機,須要對其進行充電,這時候就須要一個轉接頭把Micro USB轉爲Type-C接口,才能給手機充電。
這裏的Target就是Type-C,適配者就是Micro USB,適配器就是轉接頭,簡化實現代碼以下:

public class Test
{
    public static void main(String[] args) {
        TypeC typeC = new MicroUSBToTypeC();
        typeC.chargeWithTypeC();
    }
}

//Target:給TypeC接口的手機充電
interface TypeC
{
    void chargeWithTypeC();
}

//Adaptee:適配者,MicroUSB線
class MicroUSB
{
    public void chargeWithMicroUSB()
    {
        System.out.println("MicroUSB充電");
    }
}

//Adapter:適配器,MicroUSB到TypeC的轉接頭
class MicroUSBToTypeC implements TypeC
{
    private MicroUSB microUSB = new MicroUSB();
    @Override
    public void chargeWithTypeC()
    {
        microUSB.chargeWithMicroUSB();
    }
}

3 雙向適配器

在對象適配器的使用過程當中,若是在適配器中同時包含對Target類和Adaptee類的引用,Adaptee類能夠經過適配器調用Target類中的方法,Target類也能夠經過適配器調用Adaptee類的方法,那麼該適配器就是一個雙向適配器。例子以下:

public class Test
{
    public static void main(String[] args) {
        Adapter adapter = new Adapter();
        adapter.request();
        adapter.specificRequest();
    }
}

//適配者
interface Adaptee
{
    void specificRequest();
}

//Target類
interface Target
{
    void request();
}

//Target實現
class TargetImpl implements Target
{
    @Override
    public void request()
    {
        System.out.println("Target方法");
    }
}

//適配者實現
class AdapteeImpl implements Adaptee
{
    @Override
    public void specificRequest()
    {
        System.out.println("Adaptee方法");
    }
}

//適配器
class Adapter implements Adaptee,Target
{
    private Target target = new TargetImpl();
    private Adaptee adaptee = new AdapteeImpl();
    @Override
    public void request()
    {
        //Target的方法調用適配者方法
        adaptee.specificRequest();
    }

    @Override
    public void specificRequest()
    {
        //適配者方法調用Target的方法
        target.request();        
    }
}

4 缺省適配器

4.1 定義

缺省適配器:當不須要實現一個接口所提供的全部方法時,可先設計一個抽象類實現該接口,併爲接口中的每一個方法都提供一個默認實現(空實現),那麼該抽象類子類能夠選擇性覆蓋父類的某些方法來實現需求,它適用於不想使用一個接口中全部方法的狀況,又叫單接口適配器模式。

4.2 結構圖

在這裏插入圖片描述

4.3 角色

  • ServiceInterface(適配者接口):一般是一個聲明瞭大量方法的接口
  • AbstractServiceClass(缺省適配器類):缺省適配器模式的核心類,使用空方法的形式實現了在ServiceInterface接口中聲明的方法,一般定義爲抽象類
  • ConcreteServiceClass(具體業務類):是缺省適配器類的子類,只須要有選擇性地覆蓋適配器者中定義的方法,其餘的方法在缺省適配器類中提供了空實現

4.4 實例

Java AWT中通常能夠經過兩種方式來處理窗口事件:

  • 實現WindowListener
  • 繼承WindowAdapter

其中WindowAdapter實現了WindowListener接口,可是都是提供了空實現,也就是說實現WindowsListener的話須要實現裏面全部的方法,而繼承WindowAdapter只須要選擇性地覆蓋方法便可,結構圖:
在這裏插入圖片描述

5 主要優勢

類適配器以及對象適配器的共同優勢以下:

  • 解耦:將Target與Adaptee解耦,引入適配器來重用現有的適配者類,無須修改原有結構
  • 提升複用性:將具體的業務實現過程封裝在適配者類中,對於客戶端而言是透明的,並且提升了適配者類的複用性,同一個適配者類能夠在多個不一樣的系統複用
  • 擴展性好:能夠很方便地更換適配器,也能夠在不修改代碼的基礎上增長了新的適配器類,徹底符合開閉原則,擴展靈活

類適配器的獨有優勢以下:

  • 因爲適配器類是適配者的子類,所以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強。

對象適配器的獨有優勢以下:

  • 一個對象適配器能夠把多個不一樣的適配者適配到同一個Target
  • 能夠適配一個適配者的子類,因爲適配器與適配者之間是關聯關係,根據LSP(里氏代換原則),適配者的子類也能夠經過該適配器進行適配

6 主要缺點

類適配器缺點:

  • 對於Java,C#等不支持多重繼承的語言,一次最多隻能適配一個適配者類
  • 適配者不能是「不能繼承的類」,好比Java的final類,C#的sealed
  • 在Java,C#等Target只能是接口不能是類

對象適配器缺點:

  • 置換麻煩:相比起類適配器,在適配器中置換適配者的某些方法比較麻煩,須要先建立一個適配者類的子類,在子類將適配者類的方法置換掉,再把適配者的子類做爲真正的適配者類進行適配,實現較爲複雜

7 適用場景

  • 系統須要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需求,甚至沒有這些類的源代碼
  • 想建立一個能夠重複使用的類,用於與彼此之間沒有太大關聯的類,包括可能在未來引進的類一塊兒工做

8 總結

在這裏插入圖片描述

相關文章
相關標籤/搜索