適配器模式(Adapter Pattern)
通常,客戶類(clients of class)通過類的接口訪問它提供的服務(wù)。有時(shí),現(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)化。適配器模式建議定義一個(gè)包裝類,包裝有不兼容接口的對象。這個(gè)包裝類指的就是適配器(Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實(shí)現(xiàn)是把客戶類的請求轉(zhuǎn)化為對適配者的相應(yīng)接口的調(diào)用。換句話說:當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部調(diào)用適配者類的方法,這個(gè)過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此,適配器可以使由于借口不兼容而不能交互的類可以一起工作(work together)。
在上面討論的接口:
(1) 不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴(kuò)來定義。
(2) 不是指由窗體和GUI控件所組成的GUI應(yīng)用程序的用戶接口。
(3) 而是指類所報(bào)漏的,被其他類調(diào)用的編程接口,
類適配器(Class Adapter)VS對象適配器(Object Adapter)
適配器總體上可以分為兩類??類適配器(Class Adapter)VS對象適配器(Object Adapter)
類適配器是通過繼承類適配者類(Adaptee Class)實(shí)現(xiàn)的,另外類適配器實(shí)現(xiàn)客戶類所需要的接口。當(dāng)客戶對象調(diào)用適配器類方法的時(shí)候,適配器內(nèi)部調(diào)用它所繼承的適配者的方法。
對象適配器包含一個(gè)適配器者的引用(reference),與類適配器相同,對象適配器也實(shí)現(xiàn)了客戶類需要的接口。當(dāng)客戶對象調(diào)用對象適配器的方法的時(shí)候,對象適配器調(diào)它所包含的適配器者實(shí)例的適當(dāng)方法。
下表是類適配器(Class Adapter)和對象適配器(Object Adapter)的詳細(xì)不同:

類適配器(Class Adapter) 對象適配器(Object Adapter)
基于繼承概念 利用對象合成
只能應(yīng)用在適配者是接口,不能利用它子類的接口,當(dāng)類適配器建立時(shí),它就靜態(tài)地與適配者關(guān)聯(lián) 可以應(yīng)用在適配者是接口和它的所有子類
因?yàn)檫m配器是作為適配者的子類,所以適配器可能會重載適配者的一些行為。
注意:在JAVA中,子類不能重載父類中聲明為final的方法。 不能重載適配者的方法。
注意:字面上,不能重栽只是因?yàn)闆]有繼承。但是適配器提供包裝方法可以按需要改變行為。
客戶類對適配者中聲明為public的接口是可見的, 客戶類和適配者是完全不關(guān)聯(lián)的,只有適配器才能感知適配者接口。
在JAVA應(yīng)用程序中:
適用于期待的接口是JAVA接口的形式,而不是抽象地或具體地類的形式。這是因?yàn)?BR>JAVA編程語言只允許單繼承。因此,類適配器設(shè)計(jì)成適配者的子類。 在JAVA應(yīng)用程序中:
適用于當(dāng)客戶對象期望的接口是抽象類的形式,同時(shí)也可以應(yīng)用于期望接口是Java接口的形式。
例子:
讓我們建立一個(gè)驗(yàn)證給定客戶地址的應(yīng)用。這個(gè)應(yīng)用是作為大的客戶數(shù)據(jù)管理應(yīng)用的一部分。
讓我們定義一個(gè)Customer類:
Customer

Figure 20.1: Customer Class
Listing 20.1: Customer Class
不同的客戶對象創(chuàng)建Customer對象并調(diào)用(invoke)isValidAddress方法驗(yàn)證客戶地址的有效性。為了驗(yàn)證客戶地址的有效性,Customer類期望利用一個(gè)地址驗(yàn)證類(address validator class),這個(gè)驗(yàn)證類提供了在接口AddressValidator中聲明的接口。
Listing 20.2: AddressValidator as an Interface
讓我們定義一個(gè)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)證來自加拿大的客戶時(shí),就要對應(yīng)用進(jìn)行改進(jìn)。這需要一個(gè)驗(yàn)證加拿大客戶地址的驗(yàn)證類。讓我們假設(shè)已經(jīng)存在一個(gè)用來驗(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類提供了一個(gè)isValidCanadianAddr方法,但是Customer期望一個(gè)聲明在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方法的時(shí)候,適配器在內(nèi)部把調(diào)用傳遞給它繼承的isValidCanadianAddr方法。
在Customer類內(nèi)部,getValidator私有方法需要擴(kuò)展,以至于它可以在驗(yàn)證加拿大客戶的時(shí)候返回一個(gè)CAAdressAdapter實(shí)例。返回的對象是多態(tài)的,USAddress和CAAddressAdapter都實(shí)現(xiàn)了AddressValidator接口,所以不用改變。
Listing 20.7: Customer Class Using the CAAddressAdapter Class
CAAddressAdapter設(shè)計(jì)和對AddressValidator(聲明期望的接口)對象的多態(tài)調(diào)用使Customer可以利用接口不兼容CAAddress類提供的服務(wù)。
The class diagram in Figure 20.4 shows the overall class association.

Figure 20.4: Address Validation Application?Using Class Adapter
The sequence diagram in Figure 20.5 depicts the message flow when the CAAddressAdapter is designed as a class adapter.

Figure 20.5: Address Validation Message Flow?Using Class Adapter
作為對象適配器的地址適配器
當(dāng)討論以類適配器來實(shí)現(xiàn)地址適配器時(shí),我們說客戶類期望的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可以包含一個(gè)適配者CAAddress的一個(gè)實(shí)例。當(dāng)適配器第一次創(chuàng)建的時(shí)候,這個(gè)適配者的實(shí)例通過客戶端傳遞給適配器。通常,適配者實(shí)例可以通過下面兩種方式提供給包裝它的適配器。
(1) 對象適配器的客戶端可以傳遞一個(gè)適配者的實(shí)例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態(tài)時(shí)很適合。
(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方法時(shí), 適配器在內(nèi)部調(diào)用CAAddress(adaptee)上的isValidCanadianAddr方法。
The class diagram in Figure 20.7 shows the overall class association when the address adapter is designed as an object adapter.

Figure 20.7: Address Validation Application?Using Object Adapter
從這個(gè)例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務(wù)!
The sequence diagram in Figure 20.8 shows the message flow when the adapter CAAddressAdapter is designed as an object adapter.

Figure 20.8: Address Validation Message Flow?Using Object Adapter
附件為原文和代碼:
[img]附件:24.rar(17K) 附件:Chapter_20.doc(267K)
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請注明來處和原文作者。非常感謝。
說明:
通常,客戶類(clients of class)通過類的接口訪問它提供的服務(wù)。有時(shí),現(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)化。適配器模式建議定義一個(gè)包裝類,包裝有不兼容接口的對象。這個(gè)包裝類指的就是適配器(Adapter),它包裝的對象就是適配者(Adaptee)。適配器提供客戶類需要的接口,適配器接口的實(shí)現(xiàn)是把客戶類的請求轉(zhuǎn)化為對適配者的相應(yīng)接口的調(diào)用。換句話說:當(dāng)客戶類調(diào)用適配器的方法時(shí),在適配器類的內(nèi)部調(diào)用適配者類的方法,這個(gè)過程對客戶類是透明的,客戶類并不直接訪問適配者類。因此,適配器可以使由于借口不兼容而不能交互的類可以一起工作(work together)。
在上面討論的接口:
(1) 不是指在JAVA編程語言中接口的概念,雖然類的接口可以通過JAVA借擴(kuò)來定義。
(2) 不是指由窗體和GUI控件所組成的GUI應(yīng)用程序的用戶接口。
(3) 而是指類所報(bào)漏的,被其他類調(diào)用的編程接口,
類適配器(Class Adapter)VS對象適配器(Object Adapter)
適配器總體上可以分為兩類??類適配器(Class Adapter)VS對象適配器(Object Adapter)
類適配器:
類適配器是通過繼承類適配者類(Adaptee Class)實(shí)現(xiàn)的,另外類適配器實(shí)現(xiàn)客戶類所需要的接口。當(dāng)客戶對象調(diào)用適配器類方法的時(shí)候,適配器內(nèi)部調(diào)用它所繼承的適配者的方法。
對象適配器:
對象適配器包含一個(gè)適配器者的引用(reference),與類適配器相同,對象適配器也實(shí)現(xiàn)了客戶類需要的接口。當(dāng)客戶對象調(diào)用對象適配器的方法的時(shí)候,對象適配器調(diào)它所包含的適配器者實(shí)例的適當(dāng)方法。
下表是類適配器(Class Adapter)和對象適配器(Object Adapter)的詳細(xì)不同:

類適配器(Class Adapter) 對象適配器(Object Adapter)
基于繼承概念 利用對象合成
只能應(yīng)用在適配者是接口,不能利用它子類的接口,當(dāng)類適配器建立時(shí),它就靜態(tài)地與適配者關(guān)聯(lián) 可以應(yīng)用在適配者是接口和它的所有子類
因?yàn)檫m配器是作為適配者的子類,所以適配器可能會重載適配者的一些行為。
注意:在JAVA中,子類不能重載父類中聲明為final的方法。 不能重載適配者的方法。
注意:字面上,不能重栽只是因?yàn)闆]有繼承。但是適配器提供包裝方法可以按需要改變行為。
客戶類對適配者中聲明為public的接口是可見的, 客戶類和適配者是完全不關(guān)聯(lián)的,只有適配器才能感知適配者接口。
在JAVA應(yīng)用程序中:
適用于期待的接口是JAVA接口的形式,而不是抽象地或具體地類的形式。這是因?yàn)?BR>JAVA編程語言只允許單繼承。因此,類適配器設(shè)計(jì)成適配者的子類。 在JAVA應(yīng)用程序中:
適用于當(dāng)客戶對象期望的接口是抽象類的形式,同時(shí)也可以應(yīng)用于期望接口是Java接口的形式。
例子:
讓我們建立一個(gè)驗(yàn)證給定客戶地址的應(yīng)用。這個(gè)應(yīng)用是作為大的客戶數(shù)據(jù)管理應(yīng)用的一部分。
讓我們定義一個(gè)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對象并調(diào)用(invoke)isValidAddress方法驗(yàn)證客戶地址的有效性。為了驗(yàn)證客戶地址的有效性,Customer類期望利用一個(gè)地址驗(yàn)證類(address validator class),這個(gè)驗(yàn)證類提供了在接口AddressValidator中聲明的接口。
Listing 20.2: AddressValidator as an Interface
讓我們定義一個(gè)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)證來自加拿大的客戶時(shí),就要對應(yīng)用進(jìn)行改進(jìn)。這需要一個(gè)驗(yàn)證加拿大客戶地址的驗(yàn)證類。讓我們假設(shè)已經(jīng)存在一個(gè)用來驗(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類提供了一個(gè)isValidCanadianAddr方法,但是Customer期望一個(gè)聲明在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方法的時(shí)候,適配器在內(nèi)部把調(diào)用傳遞給它繼承的isValidCanadianAddr方法。
在Customer類內(nèi)部,getValidator私有方法需要擴(kuò)展,以至于它可以在驗(yàn)證加拿大客戶的時(shí)候返回一個(gè)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
CAAddressAdapter設(shè)計(jì)和對AddressValidator(聲明期望的接口)對象的多態(tài)調(diào)用使Customer可以利用接口不兼容CAAddress類提供的服務(wù)。
The class diagram in Figure 20.4 shows the overall class association.

Figure 20.4: Address Validation Application?Using Class Adapter
The sequence diagram in Figure 20.5 depicts the message flow when the CAAddressAdapter is designed as a class adapter.

Figure 20.5: Address Validation Message Flow?Using Class Adapter
作為對象適配器的地址適配器
當(dāng)討論以類適配器來實(shí)現(xiàn)地址適配器時(shí),我們說客戶類期望的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可以包含一個(gè)適配者CAAddress的一個(gè)實(shí)例。當(dāng)適配器第一次創(chuàng)建的時(shí)候,這個(gè)適配者的實(shí)例通過客戶端傳遞給適配器。通常,適配者實(shí)例可以通過下面兩種方式提供給包裝它的適配器。
(1) 對象適配器的客戶端可以傳遞一個(gè)適配者的實(shí)例給適配器。這種方式在選擇類的形式上有很大的靈活性,但是客戶端感知了適配者或者適配過程。這種方法在適配器不但需要適配者對象行為而且需要特定狀態(tài)時(shí)很適合。
(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方法時(shí), 適配器在內(nèi)部調(diào)用CAAddress(adaptee)上的isValidCanadianAddr方法。
The class diagram in Figure 20.7 shows the overall class association when the address adapter is designed as an object adapter.

Figure 20.7: Address Validation Application?Using Object Adapter
從這個(gè)例子可以看出,適配器可以使Customer(client)類訪問借口不兼容的CAAddress(adaptee)所提供的服務(wù)!
The sequence diagram in Figure 20.8 shows the message flow when the adapter CAAddressAdapter is designed as an object adapter.

Figure 20.8: Address Validation Message Flow?Using Object Adapter
附件為原文和代碼:
[img]附件:24.rar(17K) 附件:Chapter_20.doc(267K)
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請注明來處和原文作者。非常感謝。