通常,客户类(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)的详细不同:
补充:
类适配器(Class Adapter) 对象适配器(Object Adapter)
基于继承概念 利用对象合成
只能应用在适配者是接口,不能利用它子类的接口,当类适配器建立时,它就静态地与适配者关联 可以应用在适配者是接口和它的所有子类,因为适配器是作为适配者的子类,所以适配器可能会重载适配者的一些行为。
注意:在JAVA中,子类不能重载父类中声明为final的方法。 不能重载适配者的方法。
注意:字面上,不能重栽只是因为没有继承。但是适配器提供包装方法可以按需要改变行为。
客户类对适配者中声明为public的接口是可见的, 客户类和适配者是完全不关联的,只有适配器才能感知适配者接口。
在JAVA应用程序中:
适用于期待的接口是JAVA接口的形式,而不是抽象地或具体地类的形式。这是因为JAVA编程语言只允许单继承。因此,类适配器设计成适配者的子类。
在JAVA应用程序中:
适用于当客户对象期望的接口是抽象类的形式,同时也可以应用于期望接口是Java接口的形式。
例子:
让我们建立一个验证给定客户地址的应用。这个应用是作为大的客户数据管理应用的一部分。
让我们定义一个Customer类:
Customer
Figure 20.1: Customer Class
Listing 20.1: Customer Class
1 class
Customer {
2
3
public
static
final
String US = "US";
4
5
public
static
final
String CANADA = "Canada";
6
7
private
String address;
8
9
private
String name;
10
11
private
String zip, state, type;
12
13
public
boolean
isValidAddress() {
14
15
…
16
17
…
18
19
}
20
21
public
Customer(String inp_name, String inp_address,
22
23
String inp_zip, String
inp_state,
24
25
String inp_type) {
26
27
name = inp_name;
28
29
address = inp_address;
30
31
zip = inp_zip;
32
33
state = inp_state;
34
35
type = inp_type;
36
37
}
38
39
}//end
of class
40
41
不同的客户对象创建Customer对象并调用(invoke)isValidAddress方法验证客户地址的有效性。为了验证客户地址的有效性,Customer类期望利用一个地址验证类(address
validator class),这个验证类提供了在接口AddressValidator中声明的接口。
Listing 20.2: AddressValidator as an Interface
1 public
interface
AddressValidator {
2
3
public
boolean
isValidAddress(String inp_address,
4
5
String inp_zip, String
inp_state);
6
7
}//end
of class
8
9
让我们定义一个USAddress的验证类,来验证给定的U.S地址。
Listing 20.3: USAddress Class
1
class
USAddress implements
AddressValidator {
2
3
public
boolean
isValidAddress(String inp_address,
4
5
String inp_zip, String
inp_state) {
6
7
if
(inp_address.trim().length() < 10)
8
9
return
false;
10
11
if
(inp_zip.trim().length() < 5)
12
13
return
false;
14
15
if
(inp_zip.trim().length() > 10)
16
17
return
false;
18
19
if
(inp_state.trim().length() != 2)
20
21
return
false;
22
23
return
true;
24
25
}
26
27
}//end
of class
28
USAddress类实现AddressValidator接口,因此Customer对象使用USAddress实例作为验证客户地址过程的一部分是没有任何问题的。
Listing 20.4: Customer Class Using the USAddress Class
1 class
Customer {
2
3
…
4
5
…
6
7
public
boolean
isValidAddress() {
8
9
//get
an appropriate address validator
10
11
AddressValidator
validator = getValidator(type);
12
13
//Polymorphic
call to validate the address
14
15
return
validator.isValidAddress(address, zip, state);
16
17
}
18
19
private
AddressValidator getValidator(String custType) {
20
21
AddressValidator
validator = null;
22
23
if
(custType.equals(Customer.US)) {
24
25
validator =
new
USAddress();
26
27
}
28
29
return
validator;
30
31
}
32
33
}//end
of class
34
Figure 20.2: Customer/USAddress Validator Class Association
但是当验证来自加拿大的客户时,就要对应用进行改进。这需要一个验证加拿大客户地址的验证类。让我们假设已经存在一个用来验证加拿大客户地址的使用工具类CAAddress。
|