GeekIBLi

设计模式之美-适配器模式

2021-08-07

适配器模式

适配器模式(Adapter Pattern)又叫做变压器模式,它的功能是将一个类的接口变成客户端所期望的另一个接口,从而
使得原本因接口不匹配而导致无法在一起工作的两个类能够在一起工作,属于结构性设计模式的一种;

在软件开发的过程中,基本上任何问题都可以通过一个中间层解决。适配器模式其实就是一个中间层,适配器模式起着
转化/委托的作用,将一种接口转化为两一种服务功能或需求的接口。

下面举个例子来分析一下👇

类图

如图是一个适配器模式类图所在👆

代码

实现一个文件上传的功能,我们有很多中选择,亚马逊的AWS,阿里的OSS等等吧,但是不同的厂商有自己的标准或者API,但是在我们系统中体现的就是一个putObject方法,所以需要定义一个CloudSDK标准,然后不同的厂商来适配我们自己的标准,在我们的putObject和三方的SDK中间加一层适配层;
对于客户端来说,仅仅是完成文件上传的动作,至于你服务端到底使用亚马逊的服务还是阿里的服务,它是不care的,这也体现的策略模式; 所以这个例子兼备工厂模式+策略模式+适配器模式;

CloudController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CloudController {

// 如果是Spring项目这里直接注入就可以了,没必要这么麻烦
public CloudController(CloudService cloudService) {
this.cloudService = cloudService;
}

private CloudService cloudService;

public void uploadFile(String fileName){
System.out.println("invoke upload file service!" + fileName);
cloudService.uploadFile(fileName);
}

public static void main(String[] args) {
CloudController cloudController = new CloudController(new CloudService("ali"));
cloudController.uploadFile("think in java");
}
}

CloudService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class CloudService {

private CloudSDK cloudSDK;

public CloudService(String cloudStorage) {
// 使用工厂来创建具体的SDK
this.cloudSDK = CloudFactory.create(cloudStorage);
}

public void uploadFile(String fileName) {
cloudSDK.putObject(fileName);
}
}

CloudSDK 定义SDK标准

1
2
3
public interface CloudSDK {
void putObject(String fileName);
}

AWSSDKAdapter 适配CloudSDK标准

1
2
3
4
5
6
7
8
9
10
11
12
public class AWSSDKAdapter implements CloudSDK {
private AWSSDK awssdk;

public AWSSDKAdapter() {
this.awssdk = new AWSSDK();
}

@Override
public void putObject(String fileName) {
awssdk.putObject(fileName);
}
}

AliSDKAdapter适配CloudSDK标准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class AliSDKAdapter implements CloudSDK {
private AliSDK aliSDK;

public AliSDKAdapter() {
this.aliSDK = new AliSDK();
}

// 这个方法就是适配器的标准 不管你的三方服务需要多少接口,是要实现云上传,统一通过putObject这个接口实现就可以
@Override
public void putObject(String fileName) {
aliSDK.setBucket();
aliSDK.uploadFile(fileName);
}
}

AWSSDK 三方SDK

1
2
3
4
5
6
public class AWSSDK {
// 一般以JAR的方式引入到项目中,我们也不可能去修改三方的SDK,这是各个厂商制定的自己的标准
public void putObject(String fileName){
System.out.println("aws upload file " + fileName);
}
}

AliSDK 三方SDK

1
2
3
4
5
6
7
8
9
10
public class AliSDK {
// 一般以JAR的方式引入到项目中,我们也不可能去修改三方的SDK,这是各个厂商制定的自己的标准
public void setBucket() {
System.out.println("Ali oss set bucket!");
}

public void uploadFile(String fileName) {
System.out.println("Ali oss upload file!" + fileName);
}
}

CloudFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CloudFactory {

// 这里是写死了 生产环境中可以通过配置文件的方式或者启动加载等等方式实现,有很多
private static final Map<String, CloudSDK> sdkMap = new HashMap<>();

// 总之要符合开闭原则 对修改关闭
static {
sdkMap.put("ali", new AliSDKAdapter());
sdkMap.put("aws", new AWSSDKAdapter());
}

// 通过不同的策略生成具体的SDK实例
public static CloudSDK create(String storage) {
return sdkMap.get(storage);
}
}

适配器和装饰器模式的区别

适配器模式和装饰器模式都是包装器模式,装饰器模式其实是一种特殊的代理模式;

对比维度 适配器模式 装饰器模式
形式 没有层级关系 一种非常特别的代理模式,具有层级关系
定义 适配器和被适配者没有必然的联系,通常采用继承或代理的方式进行包装 装饰器和被装饰者都实现同一个接口,主要目的是扩展之后依旧保留OOP关系
关系 没有满足has-a的关系 满足is-a关系
功能 注重兼容、转换 注重覆盖、扩展
设计 后置考虑 前置考虑

适配器功能的优点 ✅

  • 能提高类的透明性和复用,但现有的类复用不需要改变
  • 适配器类和原角色解耦,投稿程序的扩展性
  • 在很多业务中符合开闭原则

适配器的缺点

  • 适配器编写过程需要结合业务场景综合考虑,可能会增加系统的复杂性
  • 增加代码阅读难度,降低代码可读性,过多的适配器会使得系统代码变得紊乱