适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能,让原来不兼容的两个接口协同工作。

定义

让原来不兼容的两个接口协同工作。

分类

  • 对象适配器
  • 类适配器
  • 接口适配器

角色

  • 目标接口:Target,该角色把其他类转换为我们期望的接口
  • 被适配者:Adaptee 原有的接口,也是希望被改变的接口
  • 适配器: Adapter, 将被适配者和目标接口组合到一起的类

对象适配器

使用组合的方式,Target接口是我们需要操作的接口,我们的目标也是想让原有的接口(Adaptee )变成我们想要的接口(Target),适配器类(Adapter)需要实现Target接口,Adapter类里面组合了原有的接口(Adaptee ),相当于我们在使用Target的时候,间接的通过适配器操作了原来的接口,而又没改动原来的接口,也可以根据实际业务包装原来的接口来适应我们的业务,这样就只改动我们自己写的适配器类,没有改动到原来的接口,也符合开闭原则。

类图

image-20220209171001573

代码实现

1
2
3
4
5
6
public class ObjectAdaptee {

public void specificRequest(){
System.out.println("原有的接口");
}
}
1
2
3
4
public interface Target {

void request();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObjectAdapter implements Target{

//把原有的接口组合到适配器里面
private ObjectAdaptee adaptee;

public ObjectAdapter(ObjectAdaptee adaptee){
this.adaptee = adaptee;
}

@Override
public void request() {
adaptee.specificRequest();
}
}

测试截图

image-20220209170847538

类适配器

采用继承和实现的方式实现,需要适配器类(Adapter)继承原来的接口(Adaptee )并且实现目标接口(Target),因为是继承,所以我们的适配器类其实就是原来的接口类型,能操作原来接口的属性和方法,再通过实际业务更改适配器类里面的业务,这样也是符合开闭原则的,因为又实现了目标接口(Target),所有就把原来的接口(Target)变成了我们想要的接口类型(Target)。

类图

image-20220209172805710

代码实现

1
2
3
4
5
6
public class ObjectAdaptee {

public void specificRequest(){
System.out.println("原有的接口2...");
}
}
1
2
3
4
public interface Target {

void request();
}
1
2
3
4
5
6
7
public class ObjectAdapter extends ObjectAdaptee implements Target {

@Override
public void request() {
this.specificRequest();
}
}

测试截图

image-20220209173505686

接口适配器

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求,适用于一个接口不想使用其所有的方法的情况。

类图

image-20220210102530829

代码实现

1
2
3
4
5
6
7
8
9
10
11
public interface ObjectAdaptee {

void eat();

void drink();

void paly();

void happy();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 在AbsAdapter 我们将 ObjectAdaptee 的方法进行默认实现
public abstract class AbsAdapter implements ObjectAdaptee{

@Override
public void eat() {

}

@Override
public void drink() {

}

@Override
public void paly() {

}

@Override
public void happy() {

}
}

测试截图

image-20220210094222002

双向适配器

在对象适配器的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。

类图

image-20220210100107397

代码实现

1
2
3
public interface Target {
void request();
}
1
2
3
public interface Adaptee {
void specificRequest();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Adapter implements Target,Adaptee{
//同时维持对抽象目标类和适配者的引用
private Target target;
private Adaptee adaptee;

public Adapter(Target target){
this.target = target;
}

public Adapter(Adaptee adaptee){
this.adaptee = adaptee;
}

@Override
public void specificRequest() {
adaptee.specificRequest();
}

@Override
public void request() {
target.request();
}
}

优点

  1. 可以让任何两个没有关联的类一起运行
  2. 提高了类的复用
  3. 增加了类的透明度
  4. 灵活性好

缺点

  1. 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  2. 由于 Java 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。