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

news2024/11/25 14:45:10

前言:

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

项目背景:

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

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

门面模式:

然后现在的情况是:

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

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

我们的解决办法就是让前端传一个参数: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 {

    @Autowired
    private UserService userService;


    @Override
    public 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 {

    @Autowired
    private PostService postService;

    @Autowired
    private HttpServletRequest request;

    @Override
    public 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的这个参数值是null
        Page<PostVO> postVOPage = postService.listPostVOByPage(postQueryRequest, request);
        return postVOPage;
    }
}

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

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

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

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

这里有两种方法:

一种是直接依赖注入

    @Autowired
    private HttpServletRequest request;

还有一种是通过一个RequestContextHolder

ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attrs.getRequest();
@Service
public class PictureDataSource implements DataSource{
    @Override
    public 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<>();

    @PostConstruct
    private 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.coloradmin.cn/o/2035841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI学习】具身智能的技术发展、商业路径等有趣观点

阅读了两篇有关具身智能的文章&#xff0c;有好多话语&#xff0c;挺有趣&#xff0c;做一些摘录。 一篇是&#xff1a;腾讯研究院的《具身智能的10个真问题&#xff5c;3万字圆桌实录》&#xff08;链接&#xff1a;https://mp.weixin.qq.com/s/peIi0YOJGKFV3fpLURDyyQ&#x…

一天搞定Vue3——包含Axios、ElementUI Plus、Vuex的使用!!!

前言,本篇文章是依据bilibili博主(波波酱老师)的学习笔记,波波酱老师讲的很好,很适合速成!!! 本篇文章会与vue2进行对比学习,并且也有很多的JavaScript知识点&#xff0c;要提前掌握他们才能学的效果更佳,见效更快。&#x1f973; 文章目录 Vue基础Vue的底层原理el挂载点data数…

Linux中网卡收发包的流程

进来在一个RTOS上移植开发网卡驱动&#xff0c;最终DMA收发包流程打通之后&#xff0c;在使用过程中觉得RTOS的处理逻辑太差了&#xff0c;因此有想法来梳理下Linux中对收发包流程处理&#xff0c;来给一些参考。 一、Linux接收网络包的流程 网卡是一个计算机的硬件&#xff0…

浅谈线性表——顺序表

文章目录 一、List接口二、线性表2.1、什么是线性表&#xff1f; 三、顺序表ArrayList什么是顺序表&#xff1f; 一、List接口 从上图看到List接口继承自Collection接口&#xff0c;而 ArrayList、LinkedList、Stack 类都实现了List接口&#xff0c;List是个接口&#xff0c;不…

论文新体验!分享8款人工智能AI软件论文网站

最近看到这个AI工具推广做的比较多&#xff0c;号称长文写的比kimi还要好&#xff01;难道大学生的救星下凡了&#xff1f;&#xff1f; 本文将分享8款优秀的AI软件论文网站&#xff0c;并重点推荐千笔-AIPassPaPer&#xff0c;这是一款备受用户好评的AI原创论文写作平台。 1…

C++ | 掌握C++异常处理:从基础到自定义异常体系的全面指南

09--异常 1、C语言传统的错误处理方式&#xff1a; 包括终止程序和返回错误码两种方式。 直接使用assert终止程序过于粗暴&#xff1a;用户无意的小错误也会造成程序结束运行。 return返回错误码&#xff0c;再通过错误码查找错误类型&#xff1a;过程繁琐&#xff0c;对用…

可视化基础的设计四大原则

一个好的数据可视化设计可以帮助观众迅速理解数据背后的意义。然而&#xff0c;如何确保我们的可视化设计既美观又简单易懂呢&#xff1f;本文将介绍四大设计原则——亲密原则、对比原则、对齐原则和重复原则。 1、 亲密原则&#xff08;Proximity&#xff09; 定义与应用&am…

学习大数据DAY34 面向对象思想深化练习 将从豆瓣爬取的数据置入自己搭建的网站上

目录 查看电影类型的电影列表 添加电影 修改电影 上机练习 13 使用三层架构完善 web 系统 查看电影类型的电影列表 DAL.py 文件 class MovieDAL(DBHelper): def getMovieByTid(self,typeid): sqlf"""select id,title,release_date,score,tname from Mo…

YOLOv8 | 融合改进 | C2f融合可变核卷积AKConv【附代码+小白可上手】

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 专栏目录 &#xff1a;《YOLOv8改进有效…

【JavaEE】深入理解Spring IoC与DI:从传统开发到依赖注入的转变

目录 IoC & DI ⼊⻔什么是Spring什么是容器什么是IoCIoC介绍传统程序开发问题分析解决方案IoC程序开发IoC优势 IoC & DI ⼊⻔ IoC&#xff1a;Inversion of Control (控制反转) DI&#xff1a;Dependency Injection 在前⾯我们学习了Spring Boot和Spring MVC的开发, 可…

DNS相关内容

一、dns的两种解析方式 1. 正向解析 将域名解析为 ip 地址 2. 逆向解析 将 ip 地址解析为域名 设置解析方式&#xff0c;都是在 zone 文件中 named.conf 解决权限 named.rfc1912.zone 解决解析方式 3.DNS 方向解析 把 192.168.71.145 这个 ip 地址逆向解析为 www.yuany…

Android逆向题解攻防世界-easyjava-难度6

纯Java实现&#xff0c;不涉及so, flag加密之后与指定字符串 “wigwrkaugala"比较判断&#xff0c;循环一个个字符加的&#xff0c;那可以一个个字符对应还原。 加密算法就在a,b类里面&#xff0c;代码直接复制到idea &#xff0c;枚举暴力破解。 每一位输入范围a-z , 找…

Lua脚本 快速掌握

1.Lua脚本概述 Lua是一种轻量级的编程语言&#xff0c;由巴西里约热内卢天主教大学开发。设计初衷是为了嵌入应用程序中&#xff0c;提供灵活的配置和脚本能力。Lua具有简洁的语法和强大的扩展性&#xff0c;使得它在多个领域得到了广泛应用。 Lua的特点包括动态类型、自动内…

The Sandbox 游戏制作教程第 4 章|使用装备制作游戏,触发独特互动

欢迎回到我们的系列&#xff0c;我们将记录 The Sandbox Game Maker 的 “On-Equip”&#xff08;装备&#xff09;功能的多种用途。 如果你刚加入 The Sandbox&#xff0c;On-Equip 功能是 “可收集组件”&#xff08;Collectable Component&#xff09;中的一个多功能工具&a…

C++ list【常用接口、模拟实现等】

1. list的介绍及使用 1.1 list的介绍 1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2.list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前…

MyBatisPlus 第二天

常用注解 1 TableName:数据库表名和实体类名不同时,会出现以下报错 在实体类上添加 TableName("t_user") 在开发的过程中&#xff0c;我们经常遇到以上的问题&#xff0c;即实体类所对应的表都有固定的前缀&#xff0c;例如t_或tbl_此时&#xff0c;可以使用MyBa…

el-tree自定义节点内容

<el-tree :data"data" :props"defaultProps" ref"treeRef" show-checkbox check-change"handleCheckChange"><!-- 自定义节点内容 --><template #default"{ node, data, store }"><span class"tr…

无人值守人工智能智慧系统数据分析:深度洞察与未来展望

无人值守人工智能智慧系统数据分析&#xff1a;深度洞察与未来展望 随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已逐渐渗透到社会经济的各个领域&#xff0c;其中无人值守人工智能智慧系统作为AI技术应用的前沿阵地&#xff0c;正引领着一场深刻的…

【数量关系】毛娃儿笔记

一、导学 1、比例的常见作用 &#xff08;1&#xff09;通过份数求数量 甲&#xff1a;乙1:2 那么甲乙的人数总和一定是3的倍数 &#xff08;2&#xff09;得到倍数关系 不同的说法都可以转化为比例&#xff0c;比如甲是乙的两倍2:1、甲是乙的4/34:3、甲比乙多25%5:4 &am…

基于vue框架的4S店汽车维修保养管理系统28a7y(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;客户,技师,车辆信息,财务,客户维修,维修分配,维修订单,保养预约,保养分配,保养订单,维修费用,保养费用 开题报告内容 基于Vue框架的4S店汽车维修保养管理系统 开题报告 一、项目背景与意义 随着汽车产业的迅猛发展&#xff0c;4S店作…