小結(jié):適配器模式用插座的適配器最為形象,插頭是2口的,插座是3口的,中間的適配器就是同時支持2口和三口的。從對象的角度就是一般繼承一個實(shí)現(xiàn)一個,總之,前方百計把兩者都關(guān)聯(lián)起來 。
通常,客戶類(clients?of?class)通過類的接口訪問它提供的服務(wù)。有時,現(xiàn)有的類(existing?class)可以提供客戶類的功能 需要,但是它所提供的接口不一定是客戶類所期望的。這是由于現(xiàn)有的接口太詳細(xì)或者缺乏詳細(xì)或接口的名稱與客戶類所查找的不同等諸多不同原因?qū)е碌摹?br />
在這種情況下,現(xiàn)有的接口需要轉(zhuǎn)化(convert) 為客戶類期望的接口,這樣保證了對現(xiàn)有類的重用。如果不進(jìn)行這樣的轉(zhuǎn)化,客戶類就不能利用現(xiàn)有類所提供的功能。適配器模式 (Adapter?Pattern)可以完成這樣的轉(zhuǎn)化。適配器模式建議定義一個包裝類,包裝有不兼容接口的對象。這個包裝類指的就是適配器 (Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實(shí)現(xiàn)是把客戶類的請求轉(zhuǎn)化為對適配者的相應(yīng)接 口的調(diào)用。換句話說:當(dāng)客戶類調(diào)用適配器的方法時,在適配器類的內(nèi)部調(diào)用適配者類的方法,這個過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此, 適配器可以使由于借口不兼容而不能交互的類可以一起工作(work?together)。
在上面討論的接口:
(1)????不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴(kuò)來定義。
(2)????不是指由窗體和GUI控件所組成的GUI應(yīng)用程序的用戶接口。
(3)????而是指類所報漏的,被其他類調(diào)用的編程接口,
類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
適配器總體上可以分為兩類??類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
????
類適配器是通過繼承類適配者類(Adaptee?Class)實(shí)現(xiàn)的,另外類適配器實(shí)現(xiàn)客戶類所需要的接口。當(dāng)客戶對象調(diào)用適配器類方法的時候,適配器內(nèi)部調(diào)用它所繼承的適配者的方法。
????
下表是類適配器(Class?Adapter)和對象適配器(Object?Adapter)的詳細(xì)不同:
????
例子:
讓我們建立一個驗(yàn)證給定客戶地址的應(yīng)用。這個應(yīng)用是作為大的客戶數(shù)據(jù)管理應(yīng)用的一部分。
讓我們定義一個Customer類:
Customer?
Figure?20.1:?Customer?Class?
Listing?20.1:?Customer?Class?
不同的客戶對象創(chuàng)建Customer對象并調(diào)用(invoke)isValidAddress方法驗(yàn)證客戶地址的有效性。為了驗(yàn)證客戶地址的有效性,
Customer類期望利用一個地址驗(yàn)證類(address?validator?class),這個驗(yàn)證類提供了在接口
AddressValidator中聲明的接口。
Listing?20.2:?AddressValidator?as?an?Interface?
讓我們定義一個USAddress的驗(yàn)證類,來驗(yàn)證給定的U.S地址。
Listing?20.3:?USAddress?Class?
USAddress類實(shí)現(xiàn)AddressValidator接口,因此Customer對象使用USAddress實(shí)例作為驗(yàn)證客戶地址過程的一部分是沒有任何問題的。
Listing?20.4:?Customer?Class?Using?the?USAddress?Class?
?
Figure?20.2:?Customer/USAddress?Validator?Class?Association?
但是當(dāng)驗(yàn)證來自加拿大的客戶時,就要對應(yīng)用進(jìn)行改進(jìn)。這需要一個驗(yàn)證加拿大客戶地址的驗(yàn)證類。讓我們假設(shè)已經(jīng)存在一個用來驗(yàn)證加拿大客戶地址的使用工具類CAAddress。
從下面的CAAdress類的實(shí)現(xiàn),可以發(fā)現(xiàn)CAAdress提供了客戶類Customer類所需要的驗(yàn)證服務(wù)。但是它所提供的接口不用于客戶類Customer所期望的。
Listing?20.5:?CAAdress?Class?with?Incompatible?Interface?
CAAdress類提供了一個isValidCanadianAddr方法,但是Customer期望一個聲明在AddressValidator接口中的isValidAddress方法。
接口的不兼容使得Customer對象利用現(xiàn)有的CAAdress類是困難的。一種意見是改變CAAdress類的接口,但是可能會有其他的應(yīng)用正在使用CAAdress類的這種形式。改變CAAdress類接口會影響現(xiàn)在使用CAAdress類的客戶。
應(yīng)用適配器模式,類適配器CAAdressAdapter可以繼承CAAdress類實(shí)現(xiàn)AddressValidator接口。
?
Figure?20.3:?Class?Adapter?for?the?CAAddress?Class?
Listing?20.6:?CAAddressAdapter?as?a?Class?Adapter?
因?yàn)檫m配器CAAdressAdapter實(shí)現(xiàn)了AddressValidator接口,客戶端對象訪問適配器CAAdressAdapter對象是沒 有任何問題的。當(dāng)客戶對象調(diào)用適配器實(shí)例的isValidAddress方法的時候,適配器在內(nèi)部把調(diào)用傳遞給它繼承的 isValidCanadianAddr方法。
在Customer類內(nèi)部,getValidator私有方法需要擴(kuò)展,以至于它可以 在驗(yàn)證加拿大客戶的時候返回一個CAAdressAdapter實(shí)例。返回的對象是多態(tài)的,USAddress和CAAddressAdapter都實(shí)現(xiàn) 了AddressValidator接口,所以不用改變。
Listing?20.7:?Customer?Class?Using?the?CAAddressAdapter?Class?
CAAddressAdapter設(shè)計和對AddressValidator(聲明期望的接口)對象的多態(tài)調(diào)用使Customer可以利用接口不兼容CAAddress類提供的服務(wù)。
?
Figure?20.4:?Address?Validation?Application?Using?Class?Adapter?
?
Figure?20.5:?Address?Validation?Message?Flow?Using?Class?Adapter?
作為對象適配器的地址適配器
當(dāng)討論以類適配器來實(shí)現(xiàn)地址適配器時,我們說客戶類期望的AddressValidator接口是Java接口形式。現(xiàn)在,讓我們假設(shè)客戶類期望 AddressValidator接口是抽象類而不是java接口。因?yàn)檫m配器CAAdapter必須提供抽象類AddressValidatro中聲明 的接口,適配器必須是AddressValidator抽象類的子類、實(shí)現(xiàn)抽象方法。
因?yàn)槎嗬^承在JAVA中不支持,現(xiàn)在適配器CAAddressAdapter不能繼承現(xiàn)有的CAAddress類,它已經(jīng)使用了唯一一次繼承其他類的機(jī)會。
應(yīng)用對象適配器模式,CAAddressAdapter可以包含一個適配者CAAddress的一個實(shí)例。當(dāng)適配器第一次創(chuàng)建的時候,這個適配者的實(shí)例通過客戶端傳遞給適配器。通常,適配者實(shí)例可以通過下面兩種方式提供給包裝它的適配器。
(1)????對象適配器的客戶端可以傳遞一個適配者的實(shí)例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態(tài)時很適合。
(2)????適配器可以自己創(chuàng)建適配者實(shí)例。這種方法相對來說缺乏靈活性。適用于適配器只需要適配者對象的行為而不需要適配者對象的特定狀態(tài)的情況。
?
Figure?20.6:?Object?Adapter?for?the?CAAddress?Class?
Listing?20.10:?CAAddressAdapter?as?an?Object?Adapter?
當(dāng)客戶對象調(diào)用CAAddressAdapter(adapter)上的isValidAddress方法時,?適配器在內(nèi)部調(diào)用CAAddress(adaptee)上的isValidCanadianAddr方法。
?
Figure?20.7:?Address?Validation?Application?Using?Object?Adapter?
從這個例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務(wù)!
?
Figure?20.8:?Address?Validation?Message?Flow?Using?Object?Adapter
通常,客戶類(clients?of?class)通過類的接口訪問它提供的服務(wù)。有時,現(xiàn)有的類(existing?class)可以提供客戶類的功能 需要,但是它所提供的接口不一定是客戶類所期望的。這是由于現(xiàn)有的接口太詳細(xì)或者缺乏詳細(xì)或接口的名稱與客戶類所查找的不同等諸多不同原因?qū)е碌摹?br />
在這種情況下,現(xiàn)有的接口需要轉(zhuǎn)化(convert) 為客戶類期望的接口,這樣保證了對現(xiàn)有類的重用。如果不進(jìn)行這樣的轉(zhuǎn)化,客戶類就不能利用現(xiàn)有類所提供的功能。適配器模式 (Adapter?Pattern)可以完成這樣的轉(zhuǎn)化。適配器模式建議定義一個包裝類,包裝有不兼容接口的對象。這個包裝類指的就是適配器 (Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實(shí)現(xiàn)是把客戶類的請求轉(zhuǎn)化為對適配者的相應(yīng)接 口的調(diào)用。換句話說:當(dāng)客戶類調(diào)用適配器的方法時,在適配器類的內(nèi)部調(diào)用適配者類的方法,這個過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此, 適配器可以使由于借口不兼容而不能交互的類可以一起工作(work?together)。
在上面討論的接口:
(1)????不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴(kuò)來定義。
(2)????不是指由窗體和GUI控件所組成的GUI應(yīng)用程序的用戶接口。
(3)????而是指類所報漏的,被其他類調(diào)用的編程接口,
類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
適配器總體上可以分為兩類??類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
????
類適配器:
類適配器是通過繼承類適配者類(Adaptee?Class)實(shí)現(xiàn)的,另外類適配器實(shí)現(xiàn)客戶類所需要的接口。當(dāng)客戶對象調(diào)用適配器類方法的時候,適配器內(nèi)部調(diào)用它所繼承的適配者的方法。
????
對象適配器:
對象適配器包含一個適配器者的引用(reference),與類適配器相同,對象適配器也實(shí)現(xiàn)了客戶類需要的接口。當(dāng)客戶對象調(diào)用對象適配器的方法的時候,對象適配器調(diào)它所包含的適配器者實(shí)例的適當(dāng)方法。下表是類適配器(Class?Adapter)和對象適配器(Object?Adapter)的詳細(xì)不同:
????
例子:
讓我們建立一個驗(yàn)證給定客戶地址的應(yīng)用。這個應(yīng)用是作為大的客戶數(shù)據(jù)管理應(yīng)用的一部分。
讓我們定義一個Customer類:
Customer?
Figure?20.1:?Customer?Class?
Listing?20.1:?Customer?Class?
- class?Customer?{?
- ??public?static?final?String?US?=?"US";?
- ??public?static?final?String?CANADA?=?"Canada";?
- ??private?String?address;?
- ??private?String?name;?
- ??private?String?zip,?state,?type;?
- ??public?boolean?isValidAddress()?{?
- ??????????…?
- ??????????…?
- ??}?
- ??public?Customer(String?inp_name,?String?inp_address,?
- ??????????????????String?inp_zip,?String?inp_state,?
- ??????????????????String?inp_type)?{?
- ????name?=?inp_name;?
- ????address?=?inp_address;?
- ????zip?=?inp_zip;?
- ????state?=?inp_state;?
- ????type?=?inp_type;?
- ??}?
- }//end?of?class?
Listing?20.2:?AddressValidator?as?an?Interface?
讓我們定義一個USAddress的驗(yàn)證類,來驗(yàn)證給定的U.S地址。
Listing?20.3:?USAddress?Class?
- class?USAddress?implements?AddressValidator?{?
- ??public?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state)?{?
- ???if?(inp_address.trim().length()?<?10)?
- ?????return?false;?
- ???if?(inp_zip.trim().length()?<?5)?
- ?????return?false;?
- ???if?(inp_zip.trim().length()?>?10)?
- ?????return?false;?
- ???if?(inp_state.trim().length()?!=?2)?
- ?????return?false;?
- ???return?true;?
- ??}?
- }//end?of?class?
USAddress類實(shí)現(xiàn)AddressValidator接口,因此Customer對象使用USAddress實(shí)例作為驗(yàn)證客戶地址過程的一部分是沒有任何問題的。
Listing?20.4:?Customer?Class?Using?the?USAddress?Class?
- class?Customer?{?
- ??????????…?
- ??????????…?
- ?public?boolean?isValidAddress()?{?
- ???//get?an?appropriate?address?validator?
- ???AddressValidator?validator?=?getValidator(type);?
- ???//Polymorphic?call?to?validate?the?address?
- ???return?validator.isValidAddress(address,?zip,?state);?
- ?}?
- ?private?AddressValidator?getValidator(String?custType)?{?
- ???AddressValidator?validator?=?null;?
- ???if?(custType.equals(Customer.US))?{?
- ?????validator?=?new?USAddress();?
- ???}?
- ???return?validator;?
- ?}?
- }//end?of?class?
Figure?20.2:?Customer/USAddress?Validator?Class?Association?
但是當(dāng)驗(yàn)證來自加拿大的客戶時,就要對應(yīng)用進(jìn)行改進(jìn)。這需要一個驗(yàn)證加拿大客戶地址的驗(yàn)證類。讓我們假設(shè)已經(jīng)存在一個用來驗(yàn)證加拿大客戶地址的使用工具類CAAddress。
從下面的CAAdress類的實(shí)現(xiàn),可以發(fā)現(xiàn)CAAdress提供了客戶類Customer類所需要的驗(yàn)證服務(wù)。但是它所提供的接口不用于客戶類Customer所期望的。
Listing?20.5:?CAAdress?Class?with?Incompatible?Interface?
- class?CAAddress?{?
- ??public?boolean?isValidCanadianAddr(String?inp_address,?
- ?????String?inp_pcode,?String?inp_prvnc)?{?
- ???if?(inp_address.trim().length()?<?15)?
- ?????return?false;?
- ???if?(inp_pcode.trim().length()?!=?6)?
- ?????return?false;?
- ???if?(inp_prvnc.trim().length()?<?6)?
- ?????return?false;?
- ???return?true;?
- ??}?
- }//end?of?class?
CAAdress類提供了一個isValidCanadianAddr方法,但是Customer期望一個聲明在AddressValidator接口中的isValidAddress方法。
接口的不兼容使得Customer對象利用現(xiàn)有的CAAdress類是困難的。一種意見是改變CAAdress類的接口,但是可能會有其他的應(yīng)用正在使用CAAdress類的這種形式。改變CAAdress類接口會影響現(xiàn)在使用CAAdress類的客戶。
應(yīng)用適配器模式,類適配器CAAdressAdapter可以繼承CAAdress類實(shí)現(xiàn)AddressValidator接口。
?
Figure?20.3:?Class?Adapter?for?the?CAAddress?Class?
Listing?20.6:?CAAddressAdapter?as?a?Class?Adapter?
因?yàn)檫m配器CAAdressAdapter實(shí)現(xiàn)了AddressValidator接口,客戶端對象訪問適配器CAAdressAdapter對象是沒 有任何問題的。當(dāng)客戶對象調(diào)用適配器實(shí)例的isValidAddress方法的時候,適配器在內(nèi)部把調(diào)用傳遞給它繼承的 isValidCanadianAddr方法。
在Customer類內(nèi)部,getValidator私有方法需要擴(kuò)展,以至于它可以 在驗(yàn)證加拿大客戶的時候返回一個CAAdressAdapter實(shí)例。返回的對象是多態(tài)的,USAddress和CAAddressAdapter都實(shí)現(xiàn) 了AddressValidator接口,所以不用改變。
Listing?20.7:?Customer?Class?Using?the?CAAddressAdapter?Class?
- class?Customer?{?
- ??????????…?
- ??????????…?
- ??public?boolean?isValidAddress()?{?
- ????//get?an?appropriate?address?validator?
- ????AddressValidator?validator?=?getValidator(type);?
- ????//Polymorphic?call?to?validate?the?address?
- ????return?validator.isValidAddress(address,?zip,?state);?
- ??}?
- ??private?AddressValidator?getValidator(String?custType)?{?
- ????AddressValidator?validator?=?null;?
- ????if?(custType.equals(Customer.US))?{?
- ??????validator?=?new?USAddress();?
- ????}?
- ????if?(type.equals(Customer.CANADA))?{?
- ??????validator?=?new?CAAddressAdapter();?
- ????}?
- ????return?validator;?
- ??}?
- }//end?of?class?
?
Figure?20.4:?Address?Validation?Application?Using?Class?Adapter?
?
Figure?20.5:?Address?Validation?Message?Flow?Using?Class?Adapter?
作為對象適配器的地址適配器
當(dāng)討論以類適配器來實(shí)現(xiàn)地址適配器時,我們說客戶類期望的AddressValidator接口是Java接口形式。現(xiàn)在,讓我們假設(shè)客戶類期望 AddressValidator接口是抽象類而不是java接口。因?yàn)檫m配器CAAdapter必須提供抽象類AddressValidatro中聲明 的接口,適配器必須是AddressValidator抽象類的子類、實(shí)現(xiàn)抽象方法。
- Listing?20.8:?AddressValidator?as?an?Abstract?Class?
- public?abstract?class?AddressValidator?{?
- ??public?abstract?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state);?
- }//end?of?class?
- Listing?20.9:?CAAddressAdapter?Class?
- class?CAAddressAdapter?extends?AddressValidator?{?
- ??????????…?
- ??????????…?
- ??public?CAAddressAdapter(CAAddress?address)?{?
- ????objCAAddress?=?address;?
- ??}?
- ??public?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state)?{?
- ??????????…?
- ??????????…?
- ??}?
- }//end?of?class?
因?yàn)槎嗬^承在JAVA中不支持,現(xiàn)在適配器CAAddressAdapter不能繼承現(xiàn)有的CAAddress類,它已經(jīng)使用了唯一一次繼承其他類的機(jī)會。
應(yīng)用對象適配器模式,CAAddressAdapter可以包含一個適配者CAAddress的一個實(shí)例。當(dāng)適配器第一次創(chuàng)建的時候,這個適配者的實(shí)例通過客戶端傳遞給適配器。通常,適配者實(shí)例可以通過下面兩種方式提供給包裝它的適配器。
(1)????對象適配器的客戶端可以傳遞一個適配者的實(shí)例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態(tài)時很適合。
(2)????適配器可以自己創(chuàng)建適配者實(shí)例。這種方法相對來說缺乏靈活性。適用于適配器只需要適配者對象的行為而不需要適配者對象的特定狀態(tài)的情況。
?
Figure?20.6:?Object?Adapter?for?the?CAAddress?Class?
Listing?20.10:?CAAddressAdapter?as?an?Object?Adapter?
- class?CAAddressAdapter?extends?AddressValidator?{?
- ??private?CAAddress?objCAAddress;?
- ??public?CAAddressAdapter(CAAddress?address)?{?
- ????objCAAddress?=?address;?
- ??}?
- ??public?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state)?{?
- ????return?objCAAddress.isValidCanadianAddr(inp_address,?
- ???????????inp_zip,?inp_state);?
- ??}?
- }//end?of?class?
當(dāng)客戶對象調(diào)用CAAddressAdapter(adapter)上的isValidAddress方法時,?適配器在內(nèi)部調(diào)用CAAddress(adaptee)上的isValidCanadianAddr方法。
?
Figure?20.7:?Address?Validation?Application?Using?Object?Adapter?
從這個例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務(wù)!
?
Figure?20.8:?Address?Validation?Message?Flow?Using?Object?Adapter