[设计模式]-结构型-适配器模式
[设计模式]-结构型-适配器模式
基本概念
概念说明
适配器模式的主要作⽤就是把原本不兼容的接⼝,通过适配修改做到统⼀。使得⽤户⽅便使⽤,就像我们提到的万能充、数据线、MAC笔记本的转换头、出国旅游买个插座等等,他们都是为了适配各种不同的口做的兼容
在业务开发中我们会经常的需要做不同接⼝的兼容,尤其是中台服务,中台需要把各个业务线的各种类型服务做统⼀包装,再对外提供接⼝进⾏使⽤。⽽这在我们平常的开发中也是⾮常常⻅的。
场景案例分析
类似数据源接入场景
场景分析
概念说明:本场景中使用适配器模式,通过转换,让两个系统能够完成对接。例如要调用一个对方接口,但是对方接口提供的调用参数和自身项目预期的参数不一致,因此借助适配器模式进行转化,可以和现实场景的转换器对照。
为什么要定制统一的规范,可以适当避免一些盲目接入接口的场景
定制统一的数据源接入规范(标准) :
什么数据源允许接入?
数据源接入时要满足什么要求?
需要接入方注意什么事情?
本系统要求:任何接入我们系统的数据,它必须要能够根据关键词搜索、并且支持分页搜索。通过声明接口的方式来定义规范。
❓问题扩展:假如说数据源已经支持了搜索,但是原有的方法参数和规范不一致,怎么办?
例如此处提供了Picture、User、Post等多个数据源,但其中Post是接入本地数据库进行查找,需要对请求信息进行校验 ,但这个request又不是各个数据搜索所必须的条件,遇到这种情况如何去做相应处理
【1】最直接了断的方式:不满足规范考虑剔除,对接参数需求额外提供接口/方法进行处理
【2】想办法解决参数问题:尽量自主获取到参数信息,此处借助RequestContextHolder获取请求信息从而拿到所需数据,但也会引申一个问题:当请求来源不同的时候这个request可能和系统所需的有所出入(或者如果借助shiro等一些权限校验框架,可以考虑通过其提供的工具类获取)
【3】有待考究的方式:修改规范,确认其他接口是否也是需要这个参数,但这个改造成本可能在后期会显得大,因为一些现有的接口已经按照既定规范执行,唯恐牵一发动全身
上述方式都是基于不同场景的考虑,需要结合实际选择一种改动最优的方式去解决,由于一开始就统一了规范,要规避一些已经上线结构因规范调整而牵动的联动
适配器模式:
针对不同数据源接入。定义DataSource接口和search接口方法(约定查找参数和返回结果),不同的数据源接入可通过实现DataSource接口方法完成不同的检索操作
// 1.定义DataSource接口统一规范,提供方法入口:统一入参和出参
public interface DataSource<T> {
/**
* 搜索(确定入参、出参):例如此处根据查找类型
*/
void doSearch(String searchText);
}
// 2.多个不同DataSource实现
public class XXXDataSource implements DataSource<Picture> {
@Override
public void doSearch(String searchText) {
return null;
}
}
// 3.项目中应用:获取到指定数据源,随后调用对应的doSearch方法
DataSource dataSource = new XXXDataSource();
dataSource.doSearch("xxxx");
设计实现
实现:定义DataSource接口统一接口方法规范,不同数据源接入PictureDataSource、UserDataSource、PostDataSource实现接口实现对应业务逻辑封装。如果说要接入新的数据源,则定义xxxDataSource去实现相应的接口方法
// 1.定义DataSource接口统一规范,提供方法入口:统一入参和出参
// 入参:查询条件、出参:分页数据(为了便于前后端统一分页数据统一为dataList)
public interface DataSource<T> {
/**
* 搜索
*/
Page<T> doSearch(String searchText, long pageNum, long pageSize);
}
// 2.多个不同DataSource实现
public class PictureDataSource implements DataSource<Picture> {
@Override
public Page<Picture> doSearch(String searchText, long pageNum, long pageSize) {
return null;
}
}
public class UserDataSource implements DataSource<User> {
@Override
public Page<User> doSearch(String searchText, long pageNum, long pageSize) {
return null;
}
}
public class PostDataSource implements DataSource<Post> {
@Override
public Page<Post> doSearch(String searchText, long pageNum, long pageSize) {
return null;
}
}
// 3.响应交互:对外提供接口,根据入参确认访问的数据库(伪代码参考)
public BaseResponse<SearchVO> searchAllByCondAdaptor(@RequestBody SearchRequest searchRequest, HttpServletRequest request) {
// a.参数校验
// b.根据不同数据源进行接入
// c.封装响应数据并返回
----------------------------------------数据源接入参考----------------------------------------------------------------
DataSource dataSource = null;
// 根据Type类别分别处理
switch (searchTypeEnum){
case PICTURE:
// 指定图片数据源
dataSource = pictureDataSource;
break;
case USER:
// 指定用户数据源
dataSource = userDataSource;
break;
case POST:
// 指定文章数据源
dataSource = postDataSource;
break;
default:
}
// 根据数据源调用适配器方法获取相应的分页数据
Page page = dataSource.doSearch(searchText, searchRequest.getCurrent(), searchRequest.getPageSize());
// 最终将查询到的数据信息进行封装并返回
return 封装后的响应数据;
----------------------------------------------------------------------------------------------------------------
}
}
// 4.业务扩展:需要接入新的数据源(例如此处需要接入视频数据源,则只需要实现相应的DataSource规范的接口)
-- a.接入新数据源
public class VideoDataSource implements DataSource<Object> {
@Override
public Page<Object> doSearch(String searchText, long pageNum, long pageSize) {
return null;
}
}
-- b.业务逻辑引申处理
// 参考步骤3中controller层处理,只需要在条件语句中补充新接入的数据源即可
switch (searchTypeEnum){
case PICTURE:
// 指定图片数据源
dataSource = pictureDataSource;
break;
case VIDEO:
// 新接入视频数据源
dataSource = videoDataSource;
break;
case MORE:
// 更多数据源接入
dataSource = moreDataSource;
break;
default:
}
// 根据数据源调用适配器方法获取相应的分页数据
Page page = dataSource.doSearch(searchText, searchRequest.getCurrent(), searchRequest.getPageSize());