小結:適配器模式用插座的適配器最為形象,插頭是2口的,插座是3口的,中間的適配器就是同時支持2口和三口的。從對象的角度就是一般繼承一個實現一個,總之,前方百計把兩者都關聯起來 。
通常,客戶類(clients?of?class)通過類的接口訪問它提供的服務。有時,現有的類(existing?class)可以提供客戶類的功能
需要,但是它所提供的接口不一定是客戶類所期望的。這是由于現有的接口太詳細或者缺乏詳細或接口的名稱與客戶類所查找的不同等諸多不同原因導致的。
在這種情況下,現有的接口需要轉化(
convert)
為客戶類期望的接口,這樣保證了對現有類的重用。如果不進行這樣的轉化,客戶類就不能利用現有類所提供的功能。適配器模式
(Adapter?Pattern)可以完成這樣的轉化。適配器模式建議定義一個包裝類,包裝有不兼容接口的對象。這個包裝類指的就是適配器
(Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實現是把客戶類的請求轉化為對適配者的相應接
口的調用。換句話說:當客戶類調用適配器的方法時,在適配器類的內部調用適配者類的方法,這個過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此,
適配器可以使由于借口不兼容而不能交互的類可以一起工作(work?together)。
在上面討論的接口:
(1)????不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴來定義。
(2)????不是指由
窗體和GUI控件所組成的GUI應用程序的用戶接口。
(3)????而是指類所報漏的,被其他類調用的編程接口,
類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
適配器總體上可以分為兩類??類適配器(Class?Adapter)VS對象適配器(Object?Adapter)
????
類適配器:
類適配器是通過繼承類適配者類(Adaptee?Class)實現的,另外類適配器實現客戶類所需要的接口。當客戶對象調用適配器類方法的時候,適配器內部調用它所繼承的適配者的方法。
????
對象適配器:
對象適配器包含一個適配器者的引用(
reference),與類適配器相同,對象適配器也實現了客戶類需要的接口。當客戶對象調用對象適配器的方法的時候,對象適配器調它所包含的適配器者實例的適當方法。
下表是類適配器(Class?Adapter)和對象適配器(Object?Adapter)的詳細不同:

????
例子:
讓我們建立一個驗證給定客戶地址的應用。這個應用是作為大的客戶數據管理應用的一部分。
讓我們定義一個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?
不同的客戶對象創(chuàng)建Customer對象并調用(invoke)isValidAddress方法驗證客戶地址的有效性。為了驗證客戶地址的有效性,
Customer類期望利用一個地址驗證類(address?validator?class),這個驗證類提供了在接口
AddressValidator中聲明的接口。
Listing?20.2:?AddressValidator?as?an?Interface?
- public?interface?AddressValidator?{?
- ??public?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state);?
- }//end?of?class?
讓我們定義一個USAddress的驗證類,來驗證給定的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類實現AddressValidator接口,因此Customer對象使用USAddress實例作為驗證客戶地址過程的一部分是沒有任何問題的。
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?
但是當驗證來自加拿大的客戶時,就要對應用進行改進。這需要一個驗證加拿大客戶地址的驗證類。讓我們假設已經存在一個用來驗證加拿大客戶地址的使用工具類CAAddress。
從下面的CAAdress類的實現,可以發(fā)現CAAdress提供了客戶類Customer類所需要的驗證服務。但是它所提供的接口不用于客戶類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對象利用現有的CAAdress類是困難的。一種意見是改變CAAdress類的接口,但是可能會有其他的應用正在使用CAAdress類的這種形式。改變CAAdress類接口會影響現在使用CAAdress類的客戶。
應用適配器模式,類適配器CAAdressAdapter可以繼承CAAdress類實現AddressValidator接口。
?

Figure?20.3:?Class?Adapter?for?the?CAAddress?Class?
Listing?20.6:?CAAddressAdapter?as?a?Class?Adapter?
- public?class?CAAddressAdapter?extends?CAAddress?
- ??implements?AddressValidator?{?
- ??public?boolean?isValidAddress(String?inp_address,?
- ?????String?inp_zip,?String?inp_state)?{?
- ????return?isValidCanadianAddr(inp_address,?inp_zip,?
- ???????????inp_state);?
- ??}?
- }//end?of?class?
因為適配器CAAdressAdapter實現了AddressValidator接口,客戶端對象訪問適配器CAAdressAdapter對象是沒
有任何問題的。當客戶對象調用適配器實例的isValidAddress方法的時候,適配器在內部把調用傳遞給它繼承的
isValidCanadianAddr方法。
在Customer類內部,getValidator私有方法需要擴展,以至于它可以
在驗證加拿大客戶的時候返回一個CAAdressAdapter實例。返回的對象是多態(tài)的,USAddress和CAAddressAdapter都實現
了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?
CAAddressAdapter設計和對AddressValidator(聲明期望的接口)對象的多態(tài)調用使Customer可以利用接口不兼容CAAddress類提供的服務。
?

Figure?20.4:?Address?Validation?Application?Using?Class?Adapter?
?

Figure?20.5:?Address?Validation?Message?Flow?Using?Class?Adapter?
作為對象適配器的地址適配器
當討論以類適配器來實現地址適配器時,我們說客戶類期望的AddressValidator接口是Java接口形式。現在,讓我們假設客戶類期望
AddressValidator接口是抽象類而不是java接口。因為適配器CAAdapter必須提供抽象類AddressValidatro中聲明
的接口,適配器必須是AddressValidator抽象類的子類、實現抽象方法。
- 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?
因為多繼承在JAVA中不支持,現在適配器CAAddressAdapter不能繼承現有的CAAddress類,它已經使用了唯一一次繼承其他類的機會。
應用對象適配器模式,CAAddressAdapter可以包含一個適配者CAAddress的一個實例。當適配器第一次創(chuàng)建的時候,這個適配者的實例通過客戶端傳遞給適配器。通常,適配者實例可以通過下面兩種方式提供給包裝它的適配器。
(1)????對象適配器的客戶端可以傳遞一個適配者的實例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態(tài)時很適合。
(2)????適配器可以自己創(chuàng)建適配者實例。這種方法相對來說缺乏靈活性。適用于適配器只需要適配者對象的行為而不需要適配者對象的特定狀態(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?
當客戶對象調用CAAddressAdapter(adapter)上的isValidAddress方法時,?適配器在內部調用CAAddress(adaptee)上的isValidCanadianAddr方法。
?

Figure?20.7:?Address?Validation?Application?Using?Object?Adapter?
從這個例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務!
?

Figure?20.8:?Address?Validation?Message?Flow?Using?Object?Adapter