聚合平台项目优化(门面模式,适配器模式,注册器模式)

前言:

这篇文章的思路就是抛出问题,再思考解决方案,最后利用设计模式解决问题

项目背景:

聚合搜索平台的主要功能就是一个有强大搜索能力的一个项目

用户输入一个词,同时可以搜索出用户,文章和图片这种功能

门面模式:

然后现在的情况是:

用户输入一个词,我们是将三个部分全都搜索出来

但是我们怎么样能让前端又能一次搜出所有数据、又能够分别获取某一类数据(比如分页场景)。

我们的解决办法就是让前端传一个参数:type

枚举type:比如有user用户,post文章,picture文章

这就引出了这篇文章的第一个设计模式:门面模式

门面模式(Facade Pattern)是一种结构性设计模式,它为复杂子系统提供一个简化的接口。通过创建一个门面类,门面模式可以隐藏系统的复杂性,使得外部调用变得更简单。

主要特点
  1. 简化接口:门面模式通过搭建一个统一的接口来简化对底层复杂系统的访问。
  2. 降低耦合:它减少了客户端与复杂系统之间的依赖性,客户端只需要与门面交互,而不需要直接与内部组件进行交互。
  3. 提高可维护性:由于系统内部的细节被封装在门面类中,后期对系统进行修改时,客户代码无需修改。

用户无需去理会后端业务逻辑有多复杂,只需要知道自己需要获取那一种的信息

这也有点像微服务项目的网关

用户不需要知道那么多服务器的ip地址

只需要把自己的请求发送给网关即可。

我们来看代码:

具体代码实现:

首先我们需要有一个SearchController来接收请求

还要有一个dto和vo用来接收请求参数和作为参数返回给前端。

还有一个状态的枚举

@Data
public class SearchRequest implements Serializable {/*** 查询*/private String searchText;/*** 查询接口的类型*/private String type;private static final long serialVersionUID = 1L;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SearchVO implements Serializable {private List<UserVO> userList;private List<PostVO> postList;private List<Picture> pictureList;private static final long serialVersionUID = 1L;
}
/*** 用户角色枚举** @author <a href="https://github.com/liyupi">程序员鱼皮</a>* @from <a href="https://yupi.icu">编程导航知识星球</a>*/
public enum SearchTypeEnum {POST("帖子","post"),USER("帖子","user"),PICTURE("帖子","picture");private final String text;private final String value;SearchTypeEnum(String text, String value) {this.text = text;this.value = value;}/*** 获取值列表** @return*/public static List<String> getValues() {return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());}/*** 根据 value 获取枚举** @param value* @return*/public static SearchTypeEnum getEnumByValue(String value) {if (ObjectUtils.isEmpty(value)) {return null;}for (SearchTypeEnum anEnum : SearchTypeEnum.values()) {if (anEnum.value.equals(value)) {return anEnum;}}return null;}public String getValue() {return value;}public String getText() {return text;}
}

 状态的枚举里面就三个参数:

user对应用户   post对应文章   picture对应图片

然后具体看controller层的代码:

@Component
@Slf4j
@RequiredArgsConstructor
public class SearchFacade {private final UserService userService;private final PostService postService;private final PictureService pictureService;public SearchVO searchAll(@RequestBody SearchRequest searchRequest, HttpServletRequest request){String type = searchRequest.getType();SearchTypeEnum searchTypeEnum = SearchTypeEnum.getEnumByValue(type);final String value = searchTypeEnum.getValue();ThrowUtils.throwIf(StringUtils.isBlank(value), ErrorCode.PARAMS_ERROR);//传入的值是非法的SearchVO searchVO = new SearchVO();final String searchText = searchRequest.getSearchText();if(type==null){UserQueryRequest queryRequest = new UserQueryRequest();queryRequest.setUserName(searchText);final Page<UserVO> userVOPage = userService.listUserVOByPage(queryRequest);PostQueryRequest postQueryRequest = new PostQueryRequest();postQueryRequest.setSearchText(searchText);final Page<PostVO> postVOPage = postService.listPostVOByPage(postQueryRequest,request);final Page<Picture> picturePage = pictureService.SearchPicture(searchText, 1, 10);searchVO.setUserList(userVOPage.getRecords());searchVO.setPostList(postVOPage.getRecords());searchVO.setPictureList(picturePage.getRecords());}else {switch (searchTypeEnum) {case POST -> {PostQueryRequest postQueryRequest = new PostQueryRequest();postQueryRequest.setSearchText(searchText);final Page<PostVO> postVOPage = postService.listPostVOByPage(postQueryRequest,request);searchVO.setPostList(postVOPage.getRecords());break;}case USER -> {UserQueryRequest queryRequest = new UserQueryRequest();queryRequest.setUserName(searchText);final Page<UserVO> userVOPage = userService.listUserVOByPage(queryRequest);searchVO.setUserList(userVOPage.getRecords());break;}case PICTURE -> {final Page<Picture> picturePage = pictureService.SearchPicture(searchText, 1, 10);searchVO.setPictureList(picturePage.getRecords());break;}}}return searchVO;}
}

整体的代码逻辑其实很简单:

首先就是先把参数取出来进行判断

如果传入的type不合法(就不是枚举中提到的三种)直接抛异常

然后如果是type为空,就查询全部列表

就把SearchVO里面的三个变量

private List<UserVO> userList;
private List<PostVO> postList;
private List<Picture> pictureList;

都填充上

然后如果不是,我们就用switch来判断你是想要对应哪一个service来查询

整体逻辑很简单。

适配器模式:

上面的逻辑已经可以解决问题了,不过这一篇文章是优化

所以我们还是有优化点的

我们首先还是抛出问题

上面这段代码的问题:

  1. 很冗余(重复的逻辑写了很多)
  2. 不易于扩展(如果还有其它的搜索要求,比如视频,公众号这种,想要添加进来需要再继续写逻辑(堆屎山))
  3. 如果有其它的服务想要接入这个服务非常的麻烦,怎么说这个麻烦呢,首先我们自己的每个service中的方法都千奇百怪的(不过这个项目还好)如果再加入其它服务,就很乱

综上所诉,我们的解决办法就是需要一个标准,就是每个人接口都按照我这个标准给我东西,然后你才能接入。

这就涉及到了适配器模式:

适配器模式(Adapter Pattern)是一种结构性设计模式,旨在解决不兼容接口之间的交互问题。适配器模式通过创建一个适配器类,使得原本由于接口不兼容而无法一起工作的类可以协同工作。

可以理解为生活中的转换头。

主要特点
  1. 接口转换:适配器模式允许将一个类的接口转换成客户端所期望的另一种接口。
  2. 解耦:客户端和被适配者之间的耦合度降低,客户端无需了解被适配者的具体实现。
  3. 增强系统的灵活性:通过适配器,系统可以灵活地引入新的类,而无需改动已有的代码。
适用场景
  • 当你想使用一些现有的类,而它们的接口不符合你的要求时。
  • 你想创建一个可复用的类,该类可以与其他不相关的类一起工作。
  • 系统需要与一些接口不兼容的类进行交互时。

我们的具体实现就是需要一个接口:

public interface DataSource<T> {public Page<T> doSearch(String searchText,long pageNum,long pageSize);
}

里面有一个方法doSearch方法

里面有三个参数:搜索内容,分页号,分页大小

项目中的每一个Service都需要实现这个接口,都需要向我传入这三个参数

这就是一个标准

我们根据上面的枚举,我们需要三个Service来实现这个接口

我们来看具体的代码实现:

@Service
@Slf4j
public class UserDataSource implements DataSource {@Autowiredprivate UserService userService;@Overridepublic Page doSearch(String searchText, long pageNum, long pageSize) {UserQueryRequest userQueryRequest = new UserQueryRequest();userQueryRequest.setUserName(searchText);userQueryRequest.setPageSize((int) pageSize);userQueryRequest.setCurrent((int) pageNum);Page<UserVO> userVOPage = userService.listUserVOByPage(userQueryRequest);return userVOPage;}
}

这里也有一个小技巧

我们这里的doSearch需要三个参数

但是UserService中查询用户的接口的参数是userQueryRequest

这和doSearch需要的参数差的有点大

所以我们可以在这个UserDataSource 里面调用UserService的listUserVOByPage方法,然后我们传入我们需要的参数即可。

@Service
@Slf4j
public class PostDataSource implements DataSource {@Autowiredprivate PostService postService;@Autowiredprivate HttpServletRequest request;@Overridepublic Page doSearch(String searchText, long pageNum, long pageSize) {PostQueryRequest postQueryRequest = new PostQueryRequest();postQueryRequest.setSearchText(searchText);postQueryRequest.setPageSize((int) pageSize);postQueryRequest.setCurrent((int) pageNum);//todo HttpRequest的这个参数值是nullPage<PostVO> postVOPage = postService.listPostVOByPage(postQueryRequest, request);return postVOPage;}
}

这里也有注意点,PostService的listPostVOByPage需要一个参数:HttpServletRequest request

(这个时候需要做出选择,看能不能通过其它办法解决,不能的话就需要考虑是否要把这个方法接入)

但是我们这里可以解决,就是在这个PostDataSource 中引入HttpServletRequest

这里就涉及到一个点:就是如何在不是controller层拿到request请求

这里有两种方法:

一种是直接依赖注入

    @Autowiredprivate HttpServletRequest request;

还有一种是通过一个RequestContextHolder

ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attrs.getRequest();
@Service
public class PictureDataSource implements DataSource{@Overridepublic Page doSearch(String searchText, long pageNum, long pageSize) {String url = String.format("https://cn.bing.com/images/search?q=%s&form=HDRSC2&first=%s",searchText,pageSize);Document doc = null;try {doc = Jsoup.connect(url).get();} catch (IOException e) {throw new BusinessException(ErrorCode.SYSTEM_ERROR);}Elements elements = doc.select(".iusc");final Elements elements1 = doc.select("a.inflnk");List<Picture> pictureList = new ArrayList<>();for (int i = 0; i < elements.size(); i++) {//获取图片final String m = elements.get(i).attr("m");Map<String,Object> map = JSONUtil.toBean(m,Map.class);final String murl = (String) map.get("murl");//获取标题final String title = elements1.get(i).attr("aria-label");Picture picture = new Picture();picture.setMurl(murl);picture.setTitle(title);pictureList.add(picture);if(pictureList.size()>=pageSize){break;}}Page<Picture> picturePage= new Page<>(pageNum,pageSize);picturePage.setRecords(pictureList);return picturePage;}
}

经过这个适配器模式之后else中的代码:

else {Map<String, DataSource> dataSourceMap = new HashMap<>();dataSourceMap.put("post",postDataSource);dataSourceMap.put("user",userDataSource);dataSourceMap.put("picture",pictureDataSource);DataSource dataSource = dataSourceMap.get(type);final Page<?> page = dataSource.doSearch(searchText, 1, 10);final List<?> records = page.getRecords();searchVO.setDatasourceList(page.getRecords());}

这里用了一个map来记录不同的适配器实现类。

注册模式:

注册模式:首先注册模式是单例模式的一个扩展

上面的代码还可以抽象:

我们根据上面写的map,创造一个datasourceregistry,进行初始化map,并且用@PostConstrucet注解来在所有的bean注册之后执行(因为map中的元素<String,Bean对象>)

@Component
@Slf4j
@RequiredArgsConstructor
public class DataSourceRegistry {private final UserDataSource userDataSource;private final PostDataSource postDataSource;private final PictureDataSource pictureDataSource;private Map<String,DataSource> dataSourceMap = new HashMap<>();@PostConstructprivate void initMap(){dataSourceMap.put("post",postDataSource);dataSourceMap.put("user",userDataSource);dataSourceMap.put("picture",pictureDataSource);}public DataSource getDataSource(String type){return dataSourceMap.get(type);}
}
这里还用到了一个@PostConstruct注解

为什么呢

我们仔细看代码

这里的map收集的都是bean对象,所以我们肯定要先注册bean对象然后才能存到map里面去

@PostConstruct的注解作用就是这个:在bean对象注册完之后再执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/399754.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI绘画: ComfyUI奥运高光时刻海报工作流,工作流拆解~

前言 点关注不迷路&#xff01; 这两天&#xff0c;阿里云的PAI ArtLab的ComfyUI新增了一个奥运高光时刻海报的工作流&#xff0c;小编测试下来&#xff0c;效果真的不错。不愧是大厂出品&#xff0c;必属精品。那么这次小编就简单梳理一下这个工作流的的各个部分&#xff0c…

基于vue框架的CKD电子病历系统nfa2e(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;患者,医生,药品信息,电子病历,临时医嘱,长期医嘱,健康科普 开题报告内容 基于Vue框架的CKD电子病历系统 开题报告 一、选题背景 随着信息技术的飞速发展和医疗信息化的深入推进&#xff0c;电子病历系统&#xff08;Electronic Medic…

分布式文件系统FastDFS入门

文章目录 一.分布式文件系统简介&#xff1a;二.FastDFS简介三.FastDFS组成Tracker ServerStorage Serverclient上传流程下载流程文件ID 四.FastDFS配置1.tracker.conf2.stroage 配置文件3.client配置文件 五.FastDFS使用六.代码实现通过execl调用客户端程序进行上传下载使用AP…

如何使用 Puppeteer 和 Node.JS 进行 Web 抓取?

什么是 Headlesschrome&#xff1f; Headless&#xff1f;是的&#xff0c;这意味着这个浏览器没有图形用户界面 (GUI)。不用鼠标或触摸设备与视觉元素交互&#xff0c;你需要使用命令行界面 (CLI) 来执行自动化操作。 Headlesschrome 和 Puppeteer 很多网页抓取工具都可适用…

成功解决7版本的数据库导入 8版本数据库脚本报错问题

我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 &#x1f393;擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号&#xff1a;热爱技术的小郑。回复 Java全套视频教程 或 前端全套视频…

访问网站显示不安全打不开怎么办如何处理

当访问网站时浏览器提示“不安全”&#xff0c;这通常是由于多种原因造成的。下面是一些常见的原因及其解决办法&#xff1a; 未启用HTTPS协议 如果网站仅使用HTTP协议&#xff0c;数据传输没有加密&#xff0c;会被浏览器标记为“不安全”。解决办法是启用HTTPS协议&#xff…

一篇文章教会你如何使用Haproxy,内含大量实战案例

1. Haproxy 介绍 HAProxy是法国开发者 威利塔罗&#xff08;Willy Tarreau&#xff09; 使用C语言编写的自由及开放源代码软件&#xff0c;是一款具备高并发&#xff08;万级以上&#xff09;、高性能的TCP和HTTP应用程序代理. HAProxy运行在当前的硬件上&#xff0c;可以支持…

5款在线伪原创改写软件,智能改写文章效果好

在这个信息爆炸的时代&#xff0c;内容创作变得愈发重要&#xff0c;而对于创作者来说&#xff0c;有时需要一些得力的伪原创改写工具来辅助我们更好地改写出高质量的内容。今天我要和大家分享5款令人惊喜的在线伪原创改写软件&#xff0c;它们以出色的智能改写效果&#xff0c…

【Kubernetes】身份认证与鉴权

一&#xff0c;认证 所有 Kubernetes 集群有两类用户&#xff1a;由Kubernetes管理的ServiceAccounts(服务账户)和(Users Accounts)普通账户。 两种账户的区别&#xff1a; 普通帐户是针对(人)用户的&#xff0c;服务账户针对Pod进程普通帐户是全局性。在集群所有namespaces…

【Ai学习】一个技巧,解决99%Comfyui报错!

前言 comfyui以极高灵活度及节点化工作流&#xff0c;深受AI绘画者追捧&#xff0c;每当新的模型开源&#xff0c;comfyui都是最先进行适配。 comfyui高度兼容性及灵活性带来丰富强大的扩展&#xff08;插件&#xff09;生态&#xff0c;同时也带来一系列插件安装的问题&…

从今年的计算机视觉比赛看风向

记第一次参加CV比赛的经历-长三角&#xff08;芜湖&#xff09;人工智能视觉算法大赛-CSDN博客 去年参赛的记录里说了&#xff1a; 最近&#xff0c;同样的由芜湖举办的比赛又上线了&#xff0c;果然&#xff1a; 2023年是这些赛题&#xff0c;典型的CV&#xff1a; 今年变成…

阴阳脚数码管

1.小故事 最近&#xff0c;我接到了一个既“清肺”又“烧脑”的新任务&#xff0c;设计一个低功耗蓝牙肺活量计。在这个项目中我们借鉴了一款蓝牙跳绳的硬件设计方案&#xff0c;特别是它的显示方案——数码管。 在电子工程领域&#xff0c;初学者往往从操作LED开始&#xff…

【网络】IP的路径选择——路由控制

目录 路由控制表 默认路由 主机路由 本地环回地址 路由控制表的聚合 网络分层 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 路由控制表 在数据通信中&#xff0c;IP地址作为网络层的标识&#xff0c;用于指定数据包的目标位置。然而&#xff0c;仅有IP地址并不足以确…

基于LPF改进的反电势观测器+锁相环PLL的永磁无感控制

导读:上期文章介绍的基于EMF+PLL的中高速永磁无感控制,其中决定转速和位置的估算精度的是反电势的获取精度。直接计算法很难保证反电势的估算精度,所以本期文章介绍一种基于LPF的改进型EMF观测器。 一、基于LPF改进的EMF观测器 传统的EMF观测器的表达式为: 注:这里重点强…

01_Electron 跨平台桌面应用开发介绍

Electron 跨平台桌面应用开发介绍 一、Electron 的介绍二、关于 NW.js 和 Electron 介绍三、搭建 Electron 的环境1、准备工作&#xff1a;2、安装 electron 环境3、查看 electron 的版本&#xff0c;electron -v 一、Electron 的介绍 Electron 是由 Github 开发的一个跨平台的…

Oracle事务是怎么练成的

什么是事务 事务是数据库管理系统执行过程的一个逻辑单位&#xff0c;由一系列有限的数据库操作序列构成&#xff0c;事务必须满足‌ACID属性。ACID理论是数据库中最重要的概念之一&#xff0c;分别代表原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consisten…

Django基础知识

文章目录 新建Django项目helloworld关联数据库admin 新建Django项目 创建django-admin startproject project_name 运行 python manage.py runserver 创建app: python manage.py startapp app_name 目录&#xff1a; 配置文件 settings.py 路由配置 urls.py 项目管理 manage.p…

极光流星大爆发

卑微仔广东持续200%含云量&#xff0c;线上观望大家分享的极光与流星共舞的神奇场景。 极光与流星相伴的瞬间&#xff0c;永远震撼于绝美的星空 开始放毒&#xff08;放图放图&#xff09;&#xff08;以下均拍摄于12日晚至13日晨这一时间段&#xff09;&#xff1a; 先驱猎光…

VisionPro二次开发学习笔记10-使用 PMAlign和Fixture固定Blob工具检测孔

使用 PMAlign和Fixture固定Blob工具检测孔 这个示例演示了如何使用 PMAlign 工具和 Fixture 工具来夹持一个 Blob 工具。示例代码将检测支架右上角孔的存在。当点击运行按钮时&#xff0c;将读取新图像。PMAlign 工具运行并生成一个 POSE 作为输出。POSE 是一个六自由度的变换…

2024年8月7日(mysql主从 )

回顾 主服务器 [rootmaster_mysql ~]# yum -y install rsync [rootmaster_mysql ~]# tar -xf mysql-8.0.33-linux-glibc2.12-x86_64.tar [rootmaster_mysql ~]# tar -xf mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz [rootmaster_mysql ~]# cp -r mysql-8.0.33-linux-glibc2.…