通常客户类(clients of class)通过类的接口访问它提供的服务有时现有的类(existing class)可以提供客户类的功能需要但是它所提供的接口不一定是客户类所期望的这是由于现有的接口太详细或者缺乏详细或接口的名称与客户类所查找的不同等诸多不同原因导致的
在这种情况下现有的接口需要转化(convert)为客户类期望的接口这样保证了对现有类的重用如果不进行这样的转化客户类就不能利用现有类所提供的功能适配器模式(Adapter Pattern)可以完成这样的转化适配器模式建议定义一个包装类包装有不兼容接口的对象这个包装类指的就是适配器(Adapter)它包装的对象就是适配者(Adaptee)适配器提供客户类需要的接口适配器接口的实现是把客户类的请求转化为对适配者的相应接口的调用换句话说当客户类调用适配器的方法时在适配器类的内部调用适配者类的方法这个过程对客户类是透明的客户类并不直接访问适配者类因此适配器可以使由于接口不兼容而不能交互的类可以一起工作(work together)
在上面讨论的接口
() 不是指在JAVA编程语言中接口的概念虽然类的接口可以通过JAVA借扩来定义
() 不是指由窗体和GUI控件所组成的GUI应用程序的用户接口
() 而是指类所暴露的被其他类调用的编程接口
类适配器(Class Adapter)VS对象适配器(Object Adapter)
适配器总体上可以分为两类类适配器(Class Adapter)VS对象适配器(Object Adapter)
类适配器
类适配器是通过继承类适配者类(Adaptee Class)实现的另外类适配器实现客户类所需要的接口当客户对象调用适配器类方法的时候适配器内部调用它所继承的适配者的方法
对象适配器
对象适配器包含一个适配器者的引用(reference)与类适配器相同对象适配器也实现了客户类需要的接口当客户对象调用对象适配器的方法的时候对象适配器调它所包含的适配器者实例的适当方法
下表是类适配器(Class Adapter)和对象适配器(Object Adapter)的详细不同
补充
类适配器(Class Adapter) 对象适配器(Object Adapter)
基于继承概念 利用对象合成
只能应用在适配者是接口不能利用它子类的接口当类适配器建立时它就静态地与适配者关联 可以应用在适配者是接口和它的所有子类因为适配器是作为适配者的子类所以适配器可能会重载适配者的一些行为
注意在JAVA中子类不能重载父类中声明为final的方法 不能重载适配者的方法
注意:字面上不能重栽只是因为没有继承但是适配器提供包装方法可以按需要改变行为
客户类对适配者中声明为public的接口是可见的 客户类和适配者是完全不关联的只有适配器才能感知适配者接口
在JAVA应用程序中
适用于期待的接口是JAVA接口的形式而不是抽象地或具体地类的形式这是因为JAVA编程语言只允许单继承因此类适配器设计成适配者的子类 在JAVA应用程序中
适用于当客户对象期望的接口是抽象类的形式同时也可以应用于期望接口是Java接口的形式
例子
让我们建立一个验证给定客户地址的应用这个应用是作为大的客户数据管理应用的一部分
让我们定义一个Customer类
Customer
Figure : Customer Class
Listing : 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
不同的客户对象创建Customer对象并调用(invoke)isValidAddress方法验证客户地址的有效性为了验证客户地址的有效性Customer类期望利用一个地址验证类(address validator class)这个验证类提供了在接口AddressValidator中声明的接口
Listing : AddressValidator as an Interface
public interface AddressValidator {
public boolean isValidAddress(String inp_address
String inp_zip String inp_state);
}//end of class
让我们定义一个USAddress的验证类来验证给定的US地址
Listing : USAddress Class
class USAddress implements AddressValidator {
public boolean isValidAddress(String inp_address
String inp_zip String inp_state) {
if (inp_addresstrim()length() < )
return false;
if (inp_ziptrim()length() < )
return false;
if (inp_ziptrim()length() > )
return false;
if (inp_statetrim()length() != )
return false;
return true;
}
}//end of class
USAddress类实现AddressValidator接口因此Customer对象使用USAddress实例作为验证客户地址过程的一部分是没有任何问题的
Listing : 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 validatorisValidAddress(address zip state);
}
private AddressValidator getValidator(String custType) {
AddressValidator validator = null;
if (custTypeequals(CustomerUS)) {
validator = new USAddress();
}
return validator;
}
}//end of class
Figure : Customer/USAddress Validator?Class Association
但是当验证来自加拿大的客户时就要对应用进行改进这需要一个验证加拿大客户地址的验证类让我们假设已经存在一个用来验证加拿大客户地址的使用工具类CAAddress
从下面的CAAdress类的实现可以发现CAAdress提供了客户类Customer类所需要的验证服务但是它所提供的接口不用于客户类Customer所期望的从下面的CAAdress类的实现可以发现CAAdress提供了客户类Customer类所需要的验证服务但是它所提供的接口不用于客户类Customer所期望的
Listing : CAAdress Class with Incompatible Interface
class CAAddress {
public boolean isValidCanadianAddr(String inp_address
String inp_pcode String inp_prvnc) {
if (inp_addresstrim()length() < )
return false;
if (inp_pcodetrim()length() != )
return false;
if (inp_prvnctrim()length() < )
return false;
return true;
}
}//end of class
CAAdress类提供了一个isValidCanadianAddr方法但是Customer期望一个声明在AddressValidator接口中的isValidAddress方法
接口的不兼容使得Customer对象利用现有的CAAdress类是困难的一种意见是改变CAAdress类的接口但是可能会有其他的应用正在使用CAAdress类的这种形式改变CAAdress类接口会影响现在使用CAAdress类的客户
应用适配器模式类适配器CAAdressAdapter可以继承CAAdress类实现AddressValidator接口
Figure : Class Adapter for the CAAddress Class
Listing : 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实例返回的对象是多态的USAddress和CAAddressAdapter都实现了AddressValidator接口所以不用改变
Listing : 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 validatorisValidAddress(address zip state);
}
private AddressValidator getValidator(String custType) {
AddressValidator validator = null;
if (custTypeequals(CustomerUS)) {
validator = new USAddress();
}
if (typeequals(CustomerCANADA)) {
validator = new CAAddressAdapter();
}
return validator;
}
}//end of class
CAAddressAdapter设计和对AddressValidator(声明期望的接口)对象的多态调用使Customer可以利用接口不兼容CAAddress类提供的服务
Figure : Address Validation Application?Using Class Adapter
Figure : Address Validation Message Flow?Using Class Adapter
作为对象适配器的地址适配器
当讨论以类适配器来实现地址适配器时我们说客户类期望的AddressValidator接口是Java接口形式现在让我们假设客户类期望AddressValidator接口是抽象类而不是java接口因为适配器CAAdapter必须提供抽象类AddressValidatro中声明的接口适配器必须是AddressValidator抽象类的子类实现抽象方法
Listing : 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 : 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的一个实例当适配器第一次创建的时候这个适配者的实例通过客户端传递给适配器通常适配者实例可以通过下面两种方式提供给包装它的适配器
() 对象适配器的客户端可以传递一个适配者的实例给适配器这种方式在选择类的形式上有很大的灵活性但是客户端感知了适配者或者适配过程这种方法在适配器不但需要适配者对象行为而且需要特定状态时很适合
() 适配器可以自己创建适配者实例这种方法相对来说缺乏灵活性适用于适配器只需要适配者对象的行为而不需要适配者对象的特定状态的情况
Figure : Object Adapter for the CAAddress Class
Listing : 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 objCAAddressisValidCanadianAddr(inp_address
inp_zip inp_state);
}
}//end of class
当客户对象调用CAAddressAdapter(adapter)上的isValidAddress方法时 适配器在内部调用CAAddress(adaptee)上的isValidCanadianAddr方法
Figure : Address Validation Application?Using Object Adapter
从这个例子可以看出适配器可以使Customer(client)类访问借口不兼容的CAAddress(adaptee)所提供的服务!