适配器模式
初步构造
在利用minio开发文件传输模块的时候,发现一个问题。如果单一的使用minio的话,考虑一种好的情况,也许之后我的博客需要上传大量数据的时候,或许我的小服务器无法承受这么大的数据量以及访问量,这时候想要切换其他的oss云服务,如果在代码中写了固定的操作,似乎之后切换会非常的繁琐。比如我想用阿里云的oss服务了,该如何切换呢?
索性在开发的时候就一并写其他oss的操作,这就会涉及到一个问题,我定义了一个接口,不同的oss服务的实现类均继承自我这个接口,那么我再使用注解注入的时候,要写名称去注入吗?
1 2 3 4 5 6 7 8
| public interface StorageService {
List<String> listBuckets(); ...... }
|
1 2 3 4 5 6 7 8 9 10 11 12
| @RestController public class FileController {
@Resource private StorageService minioStorageService;
@RequestMapping("/testGetAllBuckets") public String testGetAllBuckets() throws Exception { List<String> allBuckets = minioStorageService.listBuckets(); return allBuckets.get(0); } }
|
如果在controller类中,需要通过这种方式去引入不同的实现类,那么在之后想切换实现类,依然非常的麻烦。需要修改每一个变量的名称。
遇事不决,抽一层。
有什么方法可以让我们不用去代码里操作,就可以更改使用的oss服务呢?或许我们可以在配置文件中实现。
首先,实现类均继承 StorageService ,这个毋庸置疑。
我们现在要做的,就是再抽一层,使其可以根据我们配置文件中的变量,去找到对应的实现类进行方法的调用。
那么这就需要写一个配置类了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Configuration public class StorageConfig {
@Value("${storage.service.type}") private String storageType;
@Resource private StorageService aliyunStorageService;
@Resource private StorageService minioStorageService;
@Bean public StorageService storageService() { if("aliyun".equals(storageType)){ return aliyunStorageService; }else if("minio".equals(storageType)){ return minioStorageService; }else{ throw new IllegalArgumentException("未找到对应的文件存储处理服务类型"); } } }
|
这里我们将实现类的进行依赖注入,再通过 @Bean
注解以及配置文件中的value,去给需要注入 StorageService
依赖的其他类,根据配置文件中的值,返回对应的StorageService
实现类。
1 2 3
| storage: service: type: minio
|
接下来,咱们抽一层FileService
,让外部调用这个类,这个类即是沟通外部调用以及刚刚实现的StorageService
之间的桥梁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class FileService {
private final StorageService storageService;
public FileService(StorageService storageService) { this.storageService = storageService; }
public List<String> listBuckets(){ return storageService.listBuckets(); } }
|
构造器注入是一种常见的依赖注入方式。在FileService
类中,StorageService
是通过构造器注入的。 当Spring创建FileService
的实例时,它会查找一个可以用来注入的StorageService
实例。
这个实例是由StorageConfig
类中的storageService()
方法提供的,该方法根据配置的存储类型返回相应的StorageService
实例。
我们就可以直接用FileService
在外部进行方法调用了。
1 2 3 4 5 6 7 8 9 10 11 12
| @RestController public class FileController {
@Resource private FileService fileService;
@RequestMapping("/testGetAllBuckets") public String testGetAllBuckets() throws Exception { List<String> allBuckets = fileService.listBuckets(); return allBuckets.get(0); } }
|
这还算不上适配器模式,接下来我们进一步改进,实现简单的适配器模式
进一步改进
适配器模式是一种设计模式,它允许接口不兼容的对象能够一起工作。这种模式通常用来将一个类的接口转换成另一个客户端期望的接口。
适配器模式主要包括以下三个角色:
目标(Target):这是客户端期望的接口。客户端通过这个接口与应用的其他对象交互。
被适配者(Adaptee):这是需要适配的类。它定义了一个已存在的接口,这个接口需要适配。
适配器(Adapter):这是适配器模式的核心。适配器实现了目标接口,并在内部维护一个被适配者的实例。适配器将客户端的请求转发给被适配者。
适配器模式的主要目的是使得原本由于接口不兼容而不能一起工作的类可以一起工作。
StorageAdapter
接口就是目标接口,而AliyunStorageAdapter
和MinioStorageAdapter
类就是适配器,它们实现了StorageAdapter
接口,并提供了与StorageAdapter
接口兼容的方法。
在FileService
类中,使用了StorageAdapter
类型的字段,而不是直接使用AliyunStorageAdapter
或MinioStorageAdapter
。
这样,无论实际的存储服务是阿里云还是Minio,FileService
都可以通过相同的接口与它们交互,这就是适配器模式的应用。
具体的实现方式如下:
定义一个目标接口StorageAdapter
,这个接口定义了所有存储服务都应该实现的方法。
1 2 3 4 5 6 7 8 9 10 11
|
public interface StorageAdapter {
List<String> listBuckets(); ...... }
|
创建适配器类AliyunStorageAdapter
和MinioStorageAdapter
,这两个类实现了StorageAdapter
接口,并提供了具体的实现。
1 2 3 4 5 6 7
| public class AliyunStorageAdapter implements StorageAdapter { }
public class MinioStorageAdapter implements StorageAdapter { }
|
在FileService类中,使用StorageAdapter
类型的字段,而不是具体的AliyunStorageAdapter
或MinioStorageAdapter
。这样,无论实际的存储服务是什么,FileService都可以通过相同的接口与它们交互。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Service public class FileService {
private final StorageAdapter storageAdapter;
public FileService(StorageAdapter storageAdapter) { this.storageAdapter = storageAdapter; }
public List<String> listBuckets(){ return storageAdapter.listBuckets(); } }
|
在StorageConfig
类中,根据配置的存储类型,创建相应的StorageAdapter
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class StorageConfig {
@Value("${storage.service.type}") private String storageType;
@Bean public StorageAdapter storageAdapter() { if("aliyun".equals(storageType)){ return new AliyunStorageAdapter(); }else if("minio".equals(storageType)){ return new MinioStorageAdapter(); }else{ throw new IllegalArgumentException("未找到对应的文件存储处理服务类型"); } } }
|