设计模式——适配器模式

概念

适配器模式,将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

针对的问题

简单的说,就是需要的东西就在面前,但是却不能使用,而短时间内又无法改造它,于是我们就想办法适配它。

适配器的意思就是使得一个东西适合另一个东西的东西。

在软件开发中,在系统的数据和行为都正确,但是接口不符时,就应该考虑用适配器,目的是使控制范围之外的一个原有对象和某个接口匹配,适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况。

角色构成

举例

代码中有两个接口,分别为德标接口和国标接口,分别命名为DBSocketInterface和GBSocketInterface,此外还有两个实现类,分别为德国插座和中国插座,分别为DBSocket和GBSocket。为了提供两套接口之间的适配,我们提供了一个适配器,叫做SocketAdapter。除此之外,还有一个客户端,比如是我们去德国旅游时住的一家宾馆,叫Hotel,在这个德国旅馆中使用德国接口。

1
2
3
4
5
6
7
//德标接口 
public interface DBSocketInterface {
/**
* 这个方法的名字叫做:使用两项圆头的插口供电
*/
void powerWithTwoRound();
}
1
2
3
4
5
6
7
//德标插座实现德标接口
// 德国插座
public class DBSocket implements DBSocketInterface{
public void powerWithTwoRound(){
System.out.println("使用两项圆头的插孔供电");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//一个德国的宾馆,里面只有德国插口。
public class Hotel {
//旅馆中有一个德标的插口
private DBSocketInterface dbSocket;
public Hotel(){}
public Hotel(DBSocketInterface dbSocket) {
this.dbSocket = dbSocket;
}
//旅馆中有一个充电的功能
public void charge(){
//使用德标插口充电
dbSocket.powerWithTwoRound();
}
public void setSocket (DBSocketInterface dbSocket){
this.dbSocket = dbSocket;
}

}
1
2
3
4
5
6
7
8
9
10
11
//test class
public class Test {
public static void main(String[] args) {
//初始化一个德国插座对象, 用一个德标接口引用它
DBSocketInterface dbSoket = new DBSocket();
//创建一个旅馆对象
Hotel hotel = new Hotel(dbSoket);
//在旅馆中给手机充电
hotel.charge();
}
}

现在我去德国旅游,带去的三项扁头的手机充电器。如果没有带电源适配器,我是不能充电的,因为人家德国人一直这么使用,所以我就要自己想办法来解决问题。对应到我们的代码中,也就是说,上面的Hotel类,DBSocket类,DBSocketInterface接口都是不可变的(由德国的客户提供),如果我想使用这一套API,那么只能自己写代码解决。

1
2
3
4
5
6
7
//国标接口 
public interface GBSocketInterface {
/**
* 这个方法的名字叫做:使用三项扁头的插口供电
*/
void powerWithThreeFlat();
}
1
2
3
4
5
6
7
//中国插座实现国标接口
public class GBSocket implements GBSocketInterface{
@Override
public void powerWithThreeFlat() {
System.out.println("使用三项扁头插孔供电");
}
}

可以认为这两个东西是我带到德国去的,目前他们还不能使用,因为接口不一样。那么我必须创建一个适配器,这个适配器必须满足以下条件:

1 必须符合德国标准的接口,否则的话还是没办法插到德国插座中;

2 在调用上面实现的德标接口进行充电时,提供一种机制,将这个调用转到对国标接口的调用 。

这就要求:

1 适配器必须实现原有的旧的接口

2 适配器对象中持有对新接口的引用,当调用旧接口时,将这个调用委托给实现新接口的对象来处理,也就是在适配器对象中组合一个新接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//适配器类
public class SocketAdapter implements DBSocketInterface{ //实现旧接口
//组合新接口
private GBSocketInterface gbSocket;
/**
* 在创建适配器对象时,必须传入一个新接口的实现类
* @param gbSocket
*/
public SocketAdapter(GBSocketInterface gbSocket) {
this.gbSocket = gbSocket;
}
/**
* 将对旧接口的调用适配到新接口
*/
@Override ××××××××关键方法××××××××
public void powerWithTwoRound() {
gbSocket.powerWithThreeFlat();
}
}
1
2
3
4
5
6
7
8
9
public class TestAdapter {  
public static void main(String[] args) {
GBSocketInterface gbSocket = new GBSocket();
Hotel hotel = new Hotel();
SocketAdapter socketAdapter = new SocketAdapter(gbSocket);
hotel.setSocket(socketAdapter);
hotel.charge();
}
}
关键 适配器类在内部包装一个适合原来接口的类。

特点

适配器模式的三个特点:

1 适配器对象实现原有接口

2 适配器对象组合一个实现新接口的对象(这个对象也可以不实现一个接口,只是一个单纯的对象)

3 对适配器原有接口方法的调用被委托给新接口的实例的特定方法