设计模式-适配器模式

模式定义

将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。

适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

主要作用

提供一个转换器(适配器),将当前系统存在的一个对象转化为客户端能够访问的接口对象。

模式分类

根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种。
在对象适配器模式中,适配器与适配者之间是关联关系;
在类适配器模式中,适配器与适配者之间是继承(或实现)关系。

模式角色

  • Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

  • Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

  • Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

三种角色之间的关系如下:

假设当前系统中,客户端需要访问的是 Target 接口,但 Target 接口没有一个实例符合需求,而 Adaptee 实例符合需求;但是客户端无法直接使用 Adaptee(接口不兼容);因此,我们需要一个适配器(Adapter)来进行中转,让 Adaptee 能转化为 Target 接口形式;

模式解析

类适配器

原理

通过继承来实现适配器功能。

具体做法

让 Adapter 实现 Target 接口,并且继承 Adaptee,这样 Adapter 就具备 Target 和 Adaptee 的特性,就可以将两者进行转化;

UML类图

类适配器

使用步骤

创建Target接口

1
2
3
4
5
6
7
8
9
10
11
12
package main.java.com.study.designPatterns.adapter.classAdapter.demoOne;

/**
* @author: whb
* @description: 目标类
*/
public interface Target {
/**
* 这是源类Adapteee没有的方法
*/
public void request();
}

创建源类(Adaptee)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main.java.com.study.designPatterns.adapter.classAdapter.demoOne;

/**
* @author: whb
* @description: 源类
*/
public class Adaptee {
/**
* 需要适配的接口
*/
public void specificRequest() {
System.out.println("需要适配的接口...");
}
}

创建适配器类(Adapter)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main.java.com.study.designPatterns.adapter.classAdapter.demoOne;

/**
* @author: whb
* @description: 适配器Adapter继承自Adaptee,同时又实现了目标(Target)接口。
*/
public class Adapter extends Adaptee implements Target {
/**
* 目标接口要求调用request()这个方法名,但源类Adaptee没有方法request(),
* 因此适配器补充上这个方法名。
* 但实际上request()只是调用源类Adaptee的specificRequest()方法的内容,
* 所以适配器只是将specificRequest()方法作了一层封装,封装成Target可以调用的request()而已。
*/
@Override
public void request() {
this.specificRequest();
}
}

定义具体使用目标类

1
2
3
4
5
6
7
8
9
10
11
12
package main.java.com.study.designPatterns.adapter.classAdapter.demoOne;

/**
* @author: whb
* @description: 测试类适配器
*/
public class TestClassAdapter {
public static void main(String[] args) {
Target mAdapter = new Adapter();
mAdapter.request();
}
}

对象适配器

原理

通过组合来实现适配器功能。

具体做法

让 Adapter 实现 Target 接口,然后内部持有 Adaptee 实例,然后再 Target 接口规定的方法内转换 Adaptee 。

UML类图

对象适配器

使用步骤

创建Target接口

1
2
3
4
5
6
7
8
9
10
11
12
package main.java.com.study.designPatterns.adapter.objectAdapter.demoOne;

/**
* @author: whb
* @description: 目标类
*/
public interface Target {
/**
* 这是源类Adapteee没有的方法
*/
void request();
}

创建源类(Adaptee)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main.java.com.study.designPatterns.adapter.objectAdapter.demoOne;

/**
* @author: whb
* @description: 源类
*/
public class Adaptee {
/**
* 需要适配的接口
*/
public void specificRequest() {
System.out.println("需要适配的接口...");
}
}

创建适配器类(Adapter)

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
package main.java.com.study.designPatterns.adapter.objectAdapter.demoOne;

/**
* @author: whb
* @description: 适配器类(Adapter)(不适用继承而是委派)
*/
public class Adapter implements Target {
/**
* 直接关联被适配类
*/
private Adaptee adaptee;

/**
* 可以通过构造函数传入具体需要适配的被适配类对象
*/
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}

@Override
public void request() {
//这里使用委托的方式完成适配
this.adaptee.specificRequest();
}
}

定义具体使用目标类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main.java.com.study.designPatterns.adapter.objectAdapter.demoOne;

/**
* @author: whb
* @description: 测试对象适配
*/
public class TestObjectAdapter {

public static void main(String[] args) {
//需要先创建一个被适配类的对象作为参数
Target mAdapter = new Adapter(new Adaptee());
mAdapter.request();
}
}

优缺点

适配器模式

优点

  1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。

  2. 增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。

  3. 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

缺点

  • 过多的使用适配器,会让系统非常零乱,不易整体进行把握。

类的适配器模式

优点

  • 由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

缺点

  1. 对于Java等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;

  2. 适配者类不能为最终类,如在Java中不能为final类;

  3. 在Java等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

对象的适配器模式

优点

  1. 一个对象适配器可以把多个不同的适配者适配到同一个目标;

  2. 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。

缺点

  • 与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

应用场景

适配器的使用场景

  • 系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作

  • 多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们

类和对象适配器模式的使用场景

  1. 灵活使用时:选择对象的适配器模式
    类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。

  2. 需要同时配源类和其子类:选择对象的适配器

    • 对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了;
    • 对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
  3. 需要重新定义Adaptee的部分行为:选择类适配器

    • 对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。
    • 对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
  4. 仅仅希望使用方便时:选择类适配器

    • 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
    • 对于对象适配器,需要额外的引用来间接得到Adaptee。

本文标题:设计模式-适配器模式

文章作者:王洪博

发布时间:2018年10月04日 - 14:10

最后更新:2019年09月12日 - 10:09

原始链接:http://whb1990.github.io/posts/210c1e9b.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%