设计模式(8):适配器模式ADAPTER

适配器模式的定义

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


使用场景

以下情况使用Adapter模式

  • 你想使用一个已经存在的类,而它的接口不符合你的需求。
  • 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
  • (仅适用于对象Adapter)你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

结构

适配器模式分为类适配器和对象适配器两种

类适配器

类适配器模式的结构如下
ShowImage

对象适配器

对象适配器模式的结构如下
ShowImage

  • Target

    – 定义Client使用的与特定领域相关的接口。

  • Client

    – 与符合Target接口的对象交互。

  • Adaptee

    – 定义一个已经存在需要适配的接口。

  • Adapter

    – 将Adaptee的接口适配转换成Target目标接口

协作

Client在Adapter实例上调用一些操作。接着适配器调用Adaptee的操作实现这个请求。


效果

两种适配器模式的实现有不同的侧重。

类适配器

通过继承的方式来适配两个接口,这种方式叫作类适配器。在C++中可以通过多继承实现,但是在Java中,由于语言特性,类不能多继承,这时候可以通过接口的方式实现。

  • 类适配器会用一个具体的Adapter类对Adaptee和Target进行适配。如果想匹配一个接口以及它所有的实现类时,类Adapter就无法工作。
  • 使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个实现类。
  • 仅仅引入了一个对象,并不需要额外的引用以间接得到adaptee。

对象适配器

Adapter对象中包含了对Adaptee的引用,通过这种方式进行适配叫作对象适配器。

  • 允许一个Adapter与多个Adaptee【有可能是Adaptee本身以及它所有的子类(如果有子类需要适配的话)】同时工作。Adapter可以一次给所有的Adaptee添加功能。
  • 使得重定义Adaptee的行为比较困难。因为Adapter对象包含的是对Adaptee的对象的引用,而不是去实现某个接口或者继承。重定义就得生成Adaptee的子类并且使得Adapter引用这个子类的对象而不是去引用Adaptee本身。

适配器模式实现(Implement)

案例

现在小明有一台MacBookPro,家里有一台带有HDMI接口的Dell显示器,在家里可以通过HDMI接口将MacBookPro信号输出到Dell显示器上面进行显示。但是公司里面的三星显示器却只有VGA接口,而MacBook上面也只有HDMI接口,在公司里面想要MacBookPro连上三星显示器就出问题了。这时候只有去买一个HDMI/VGA转换器Adapter,将HDMI转换成VGA信号后,三星显示器成功显示。

类适配器


结构

使用类适配器方式的结构如下
ShowImage

代码实现

HDMI接口的interface

1
2
3
public interface HdmiPort {
void outputSignal();
}

具体的HDMI类

1
2
3
4
5
6
public class ConcreteHdmiPort implements HdmiPort {
@Override
public void outputSignal() {
System.out.println("HDMI接口输出数字信号...");
}
}

有HDMI接口的Dell显示器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DellDisplayMonitor {
private HdmiPort mHdmiPort;
public void setHdmiOutputPort(HdmiPort hdmiPort) {
mHdmiPort = hdmiPort;
}
public void display() {
if (mHdmiPort == null) {
return;
}
System.out.println("戴尔显示器收到HDMI信号,正常显示...");
}
}

MacbookPro类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MacbookPro {
private HdmiPort mHdmiPort;
public MacbookPro() {
}
public void setDigitalSignalOutputPort(HdmiPort hdmiPort) {
mHdmiPort = hdmiPort;
}
public void transferDigitalSignal() {
mHdmiPort.outputSignal();
}
}

MacBookPro直接通过HDMI接口接到Dell显示器上面正常显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void transferByHdmi() {
//将HDMI接口接上macbook
MacbookPro macbookPro = new MacbookPro();
HdmiPort hdmiPort = new HdmiPort();
macbookPro.setDigitalSignalOutputPort(hdmiPort);
//将Dell显示器接上HDMI接口
DellDisplayMonitor dellDisplayMonitor = new DellDisplayMonitor();
dellDisplayMonitor.setHdmiOutputPort(hdmiPort);
//连接好后戴尔显示器显示macbook传过来的信号
macbookPro.transferDigitalSignal();
dellDisplayMonitor.display();
}

VGA接口

1
2
3
4
5
public class VgaPort {
public void outputSignal() {
System.out.println("VGA接口输出模拟信号...");
}
}

只有VGA接口的三星显示器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SamsungDisplayMonitor {
private VgaPort mVgaOutputPort;
public void setVgaOutputPort(VgaPort vgaOutputPort) {
mVgaOutputPort = vgaOutputPort;
}
public void display() {
if (mVgaOutputPort == null) {
return;
}
System.out.println("三星显示器收到VGA信号,正常显示...");
}
}

类适配器实现

1
2
3
4
5
6
7
8
9
10
public class HdmiToVgaClassAdapter extends VgaPort implements HdmiPort {
public void transformSignal() {
System.out.println("适配器将HDMI信号转换成VGA信号...");
}
@Override
public void outputSignal() {
System.out.println("HDMI接口输出数字信号...");
}
}

通过继承(Java中不能多继承,所以实现接口)的方式实现类适配器


通过适配器成功在三星显示器上显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void transferByVga() {
MacbookPro macbookPro = new MacbookPro();
HdmiToVgaClassAdapter hdmiToVgaClassAdapter = new HdmiToVgaClassAdapter();
//将HDMI接口连上笔记本和Adapter
macbookPro.setDigitalSignalOutputPort(hdmiToVgaClassAdapter);
//将Adapter的VGA接口连上三星显示器
SamsungDisplayMonitor samsungDisplayMonitor = new SamsungDisplayMonitor();
samsungDisplayMonitor.setVgaOutputPort(hdmiToVgaClassAdapter);
//连接好后三星显示器显示macbook传过来的信号
macbookPro.transferDigitalSignal();
hdmiToVgaClassAdapter.transformSignal();
samsungDisplayMonitor.display();
}


对象适配器


结构

使用对象适配器方式的结构如下
ShowImage

代码实现

对象适配器的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HdmiToVgaObjectAdpater extends VgaPort {
private HdmiPort mHdmiPort;
public void connectLaptopWithHdmi(HdmiPort hdmiPort) {
mHdmiPort = hdmiPort;
}
public void transformSignal() {
System.out.println("适配器将HDMI信号转换成VGA信号...");
}
@Override
public void outputSignal() {
mHdmiPort.outputSignal();
}
}

其他的代码都大同小异,这里就省略其他代码了

成功显示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ObjectAdapterDemoTest {
@Test
public void transferByHdmi() {
//将HDMI接口接上macbook
MacbookPro macbookPro = new MacbookPro();
HdmiPort hdmiPort = new HdmiPort();
macbookPro.setDigitalSignalOutputPort(hdmiPort);
//将Dell显示器接上HDMI接口
DellDisplayMonitor dellDisplayMonitor = new DellDisplayMonitor();
dellDisplayMonitor.setHdmiOutputPort(hdmiPort);
//连接好后戴尔显示器显示macbook传过来的信号
macbookPro.transferDigitalSignal();
dellDisplayMonitor.display();
}
@Test
public void transferByVga() {
MacbookPro macbookPro = new MacbookPro();
HdmiPort hdmiPort = new HdmiPort();
HdmiToVgaObjectAdpater hdmiToVgaObjectAdpater = new HdmiToVgaObjectAdpater();
//将HDMI接口连上笔记本和Adapter
macbookPro.setDigitalSignalOutputPort(hdmiPort);
hdmiToVgaObjectAdpater.connectLaptopWithHdmi(hdmiPort);
//将Adapter的VGA接口连上三星显示器
SamsungDisplayMonitor samsungDisplayMonitor = new SamsungDisplayMonitor();
samsungDisplayMonitor.setVgaOutputPort(hdmiToVgaObjectAdpater);
//连接好后三星显示器显示macbook传过来的信号
macbookPro.transferDigitalSignal();
hdmiToVgaObjectAdpater.transformSignal();
samsungDisplayMonitor.display();
}
}


总结

适配器模式将客户端与需适配的接口解耦,这样就不用修改原有的代码,符合了开闭原则。并且客户端调用更灵活,只需要调用定义好的接口就可以了。但是适配器模式不能滥用,大量的使用适配器会导致代码十分零乱,所以适配器模式一般在系统趋向稳定之后小的细节需要调整适配的条件下再使用。

参考代码

------ 本文结束 ------

版权声明


BillyYccc's blog by Billy Yuan is licensed under a Creative Commons BY-NC-SA 4.0 International License.
本文原创于BillyYccc's Blog,转载请注明原作者及出处!