天天看點

重走Java設計模式——擴充卡模式(Adapter Pattern)

擴充卡模式

定義

一個類的接口轉換成客戶希望的另外一個接口。擴充卡模式使得原本由于接口不相容而不能一起工作的那些類可以一起工作。

結構詳解

擴充卡模式有類的擴充卡模式和對象的擴充卡模式兩種不同的形式。

類擴充卡模式

類的擴充卡模式把适配的類的API轉換成為目标類的API。

類擴充卡的結構圖

重走Java設計模式——擴充卡模式(Adapter Pattern)

在圖中我們可以看到Adaptee類并沒有sampleOperation2()方法,而用戶端則期待這個方法。為使用戶端能夠使用Adaptee類,提供一個中間環節,即類Adapter,把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是繼承關系,這決定了這個擴充卡模式是類的:

這個模式涉及到的角色:

  1. 目标(Target):這就是所期待得到的接口。注意:由于這裡讨論的是類擴充卡模式,是以目标不可以是類。
  2. 目标(Target):這就是所期待得到的接口。注意:由于這裡讨論的是類擴充卡模式,是以目标不可以是類。
  3. 擴充卡(Adaper):擴充卡類是本模式的核心。擴充卡把源接口轉換成目标接口。顯然,這一角色不可以是接口,而必須是具體類。

類适配的代碼執行個體

// 已存在的、具有特殊功能、但不符合我們既有的标準接口的類  
class Adaptee {  
    public void specificRequest() {  
        System.out.println("被适配類具有 特殊功能...");  
    }  
}  


// 目标接口,或稱為标準接口  
interface Target {  
    public void request();  
}  

// 具體目标類,隻提供普通功能  
class ConcreteTarget implements Target {  
    public void request() {  
        System.out.println("普通類 具有 普通功能...");  
    }  
}   


// 擴充卡類,繼承了被适配類,同時實作标準接口  
class MyAdapter extends Adaptee implements Target{  
    public void request() {  
        super.specificRequest();  
    }  
}   


// 測試類  
public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能類  
        Target concreteTarget = new ConcreteTarget();  
        concreteTarget.request();  

        // 使用特殊功能類,即适配類  
        Target adapter = new MyAdapter();  
        adapter.request();  
    }  
}  
測試結果:
普通類 具有 普通功能...
被适配類具有 特殊功能... 
           

對象擴充卡模式

與類的擴充卡模式一樣,對象的擴充卡模式把被适配的類的API轉換成為目标類的API,與類的擴充卡模式不同的是,對象的擴充卡模式不是使用繼承關系連接配接到Adaptee類,而是使用委派關系連接配接到Adaptee類。

對象擴充卡模式結構圖

重走Java設計模式——擴充卡模式(Adapter Pattern)

在圖中我們可以看出,Adaptee類并沒有sampleOperation2()方法,而用戶端則期待這個方法。為使用戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的執行個體,進而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關系,這決定了擴充卡模式是對象的。

對象擴充卡代碼執行個體

// 擴充卡類,直接關聯被适配類,同時實作标準接口  
class MyAdapter implements Target{  
    // 直接關聯被适配類  
    private Adaptee adaptee;  

    // 可以通過構造函數傳入具體需要适配的被适配類對象  
    public MyAdapter (Adaptee adaptee) {  
        this.adaptee = adaptee;  
    }  

    public void request() {  
        // 這裡是使用委托的方式完成特殊功能  
        this.adaptee.specificRequest();  
    }  
}  


// 測試類  
public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能類  
        Target concreteTarget = new ConcreteTarget();  
        concreteTarget.request();  

        // 使用特殊功能類,即适配類,  
        // 需要先建立一個被适配類的對象作為參數  
        Target adapter = new MyAdapter(new Adaptee());  
        adapter.request();  
    }  
}  
測試結果:
普通類 具有 普通功能...
被适配類具有 特殊功能... 
           

類擴充卡模式與對象擴充卡模式的對比

  1. 類擴充卡使用對象繼承的方式,是靜态的定義方式;對象擴充卡使用對象組合的方式,是動态組合的方式。
  2. 對于類擴充卡,由于擴充卡直接繼承了Adaptee,使得擴充卡不能和Adaptee的子類一起工作,因為繼承是靜态的關系,當擴充卡繼承了Adaptee後,就不可能再去處理 Adaptee的子類了;但是對象擴充卡,一個擴充卡可以把多種不同的源适配到同一個目标。換言之,同一個擴充卡可以把源類和它的子類都适配到目标接口。因為對象擴充卡采用的是對象組合的關系,隻要對象類型正确,是不是子類都無所謂。
  3. 對于類擴充卡,擴充卡可以重定義Adaptee的部分行為,相當于子類覆寫父類的部分實作方法;但是對象擴充卡,要重定義Adaptee的行為比較困難,這種情況下,需要定義Adaptee的子類來實作重定義,然後讓擴充卡組合子類。雖然重定義Adaptee的行為比較困難,但是想要增加一些新的行為則友善的很,而且新增加的行為可同時适用于所有的源。
  4. 對于類擴充卡,僅僅引入了一個對象,并不需要額外的引用來間接得到Adaptee;但是對象擴充卡,需要額外的引用來間接得到Adaptee。

擴充卡的優缺點

優點

  1. 可以讓任何兩個沒有關聯的類一起運作。;
  2. 提高了類的複用;
  3. 增加了類的透明度;
  4. 靈活性好。

缺點

  1. 過多地使用擴充卡,會讓系統非常零亂,不易整體進行把握。比如,明明看到調用的是 A 接口,其實内部被适配成了 B 接口的實作,一個系統如果太多出現這種情況,無異于一場災難。是以如果不是很有必要,可以不使用擴充卡,而是直接對系統進行重構。
  2. 由于 JAVA 至多繼承一個類,是以至多隻能适配一個适配者類,而且目标類必須是抽象類。

使用場景

有動機地修改一個正常運作的系統的接口,這時應該考慮使用擴充卡模式。

使用注意事項

擴充卡不是在詳細設計時添加的,而是解決正在服役的項目的問題。