對象的擴充卡模式
與類的擴充卡模式一樣,對象的擴充卡模式把被适配的類的API轉換成目标類的API,與類的擴充卡模式不同的是,對象的擴充卡模式不是使用繼承關系連接配接到Adaptee類,而是使用委派關系連接配接到Adaptee類。對象的擴充卡模式的靜态結構如下圖所示。
從上圖中可以看出,Adaptee類并沒有simpleOperation2()方法,而用戶端則期待這個方法。為使用戶端能夠使用Adaptee類,需要提供一個包裝(Wrapper)類Adapter。這個包裝類包裝了一個Adaptee的執行個體,進而此包裝類能夠把Adaptee的API與Target類的API銜接起來。Adapter與Adaptee是委派關系,這決定了這個擴充卡模式是對象的。
從上圖可以看出,模式所涉及的角色有:
- 目标(Target)角色:這就是所期待的接口,目标可以是具體的或抽象的類。
- 源(Adaptee)角色:現有需要适配的接口。
- 擴充卡(Adapter)角色:擴充卡類是本模式的核心。擴充卡把源接口轉換成目标接口,顯然,這一角色必須是具體類。
對象的擴充卡模式代碼
public interface Target {
/**
* 這是源類也有的方法simpleOperation1
*/
void simpleOperation1();
/**
* 這是源類沒有的方法simpleOperation2
*/
void simpleOperation2();
}
上面給出的是目标角色的源代碼,這個角色是以一個Java接口的形式實作的。可以看出,這個接口聲明了兩個方法:simpleOperation1()和simpleOperation2()。而源角色Adapatee是一個具體類,它有一個simpleOperation1()方法,但是沒有simpleOperation2()方法,如下面帶入清單所示。
public class Adaptee {
/**
* 源類含有方法simpleOperation1
*/
public void simpleOperation1(){};
}
擴充卡類的源代碼如下面代碼清單所示。
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
super();
this.adaptee = adaptee;
}
/**
* 源類有方法simpleOperation1
* 是以擴充卡類直接委派即可
*/
@Override
public void simpleOperation1() {
adaptee.simpleOperation1();
}
/**
* 源類沒有方法simpleOperation2
* 是以擴充卡類補充上這個方法
*/
@Override
public void simpleOperation2() {
//write you code here
}
}
對象的擴充卡模式的效果:
- 一個擴充卡可以把多種不同的源适配到同一個目标。換言之,同一個擴充卡可以把源類和它的子類都适配到目标接口。
- 與類的擴充卡模式相比,要想置換源類的方法就不容易。如果一定要置換掉源類的一個或多個方法,就隻好先做一個源類的子類,将源類的方法置換掉,然後再把源類的子類當作真正的源進行适配。
- 雖然想要置換源類的方法不容易,但是要想增加一些新的方法則友善得很,而且新增加的方法可同時适用于所有的源。
總結
擴充卡模式将現有接口轉化為客戶類所期望的接口,實作了對現有類的複用,它是一種使用頻率非常高的設計模式,在軟體開發中得以廣泛應用,在Spring等開源架構、驅動程式設計(如JDBC中的資料庫驅動程式)中也使用了擴充卡模式。
** 1. 主要優點**
無論是對象擴充卡模式還是類擴充卡模式都具有如下優點:
- 将目标類和适配者類解耦,通過引入一個擴充卡類來重用現有的适配者類,無須修改原有結構。
- 增加了類的透明性和複用性,将具體的業務實作過程封裝在适配者類中,對于用戶端類而言是透明的,而且提高了适配者的複用性,同一個适配者類可以在多個不同的系統中複用。
- 靈活性和擴充性都非常好,通過使用配置檔案,可以很友善地更換擴充卡,也可以在不修改原有代碼的基礎上增加新的擴充卡類,完全符合“開閉原則”。
具體來說,類擴充卡模式還有如下優點:
- 由于擴充卡類是适配者類的子類,是以可以在擴充卡類中置換一些适配者的方法,使得擴充卡的靈活性更強。
對象擴充卡模式還有如下優點:
- 一個對象擴充卡可以把多個不同的适配者适配到同一個目标;
- 可以适配一個适配者的子類,由于擴充卡和适配者之間是關聯關系,根據“裡氏代換原則”,适配者的子類也可通過該擴充卡進行适配。
** 2. 主要缺點**
類擴充卡模式的缺點如下:
- 對于Java、C#等不支援多重類繼承的語言,一次最多隻能适配一個适配者類,不能同時适配多個适配者;
- 适配者類不能為最終類,如在Java中不能為final類,C#中不能為sealed類;
- 在Java、C#等語言中,類擴充卡模式中的目标抽象類隻能為接口,不能為類,其使用有一定的局限性。
- 與類擴充卡模式相比,要在擴充卡中置換适配者類的某些方法比較麻煩。如果一定要置換掉适配者類的一個或多個方法,可以先做一個适配者類的子類,将适配者類的方法置換掉,然後再把适配者類的子類當做真正的适配者進行适配,實作過程較為複雜。
- 系統需要使用一些現有的類,而這些類的接口(如方法名)不符合系統的需要,甚至沒有這些類的源代碼。
- 想建立一個可以重複使用的類,用于與一些彼此之間沒有太大關聯的一些類,包括一些可能在将來引進的類一起工作。
- (對對象的擴充卡模式而言)在設計裡,需要改變多個已有的子類的接口,如果使用類的擴充卡模式,就要針對每一個子類做一個擴充卡類,而這不太實際。