Aurora中的策略模式和模板模式

news2024/11/23 1:34:43

Aurora中的策略模式和模板模式

在aurora中为了方便以后的扩展使用了策略模式和模板模式实现图片上传和搜索功能,能够在配置类中设置使用Oss或者minio上传图片,es或者mysql文章搜索。后续有新的上传方式或者搜索方式只需要编写对应的实现类即可,其它代码无需更改,做到解耦的效果。

什么是策略模式

策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。

策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。

  • 环境角色(Context):持有一个策略类的引用,提供给客户端使用。
  • 抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略角色(ConcreteStrategy):包装了相关的算法或行为。

在这里插入图片描述

什么是模板模式

模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。 这种类型的设计模式属于行为型模式。定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。

模板模式主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。

  • 抽象模板(Abstract Template): 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤;定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
  • 具体模板(Concrete Template): 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤;每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。

https://www.dofactory.com/images/diagrams/net/template.gif

这篇文章把这两个模式讲的很好 https://www.cnblogs.com/xuwujing/p/9954263.html

图片上传

策略模式的应用

图片上传中有两种上传策略Oss和Minio。在上传图片的时候,如果用户配置了Oss那么就选择Oss上传,否则就选择Minio上传。虽然可以用if else判断,但是每次增加新的上传方式就需要在原方法中更改,这样处理会导致难以维护。通过分析我们可以发现Oss和Minio的实现功能都是上传图片,所以我们可以用策略模式把这两个功能的上传功能抽象成一个策略类接口,再分别实现Oss和Minio的上传策略,然后定义环境角色根据配置选择具体的策略上传图片。

模板模式的应用

进一步分析两种上传策略的步骤都是

  1. 根据文件流和配置生成图片在对象存储中的路径
  2. 判断图片是否存在
  3. 不存在则上传图片
  4. 返回图片路径

所以可以定义一个抽象的模板类执行这些步骤,让不同的上传策略实现自己的上传方法和生成路径方法

Tips
  1. 图片上传时,前台传递的参数是一个MultipartFile类型的,调用getInputStream()可以得到图片的输入流,进一步上传给云存储
  2. 环境对象将所有的策略实现类保存在map中,根据yml中配置的上传方式选择对应的策略上传
	@Value("${upload.mode}")
    private String uploadMode;

    @Autowired
	// 每个策略类 都会以 @Service("ossUploadStrategyImpl") 的方式命名,然后被自动注入到map中
    private Map<String, UploadStrategy> uploadStrategyMap;

    public String executeUploadStrategy(MultipartFile file, String path) {
        return uploadStrategyMap.get(getStrategy(uploadMode)).uploadFile(file, path);
    }

文章搜索

文章搜素中有两种搜索模式 Es 和 Mysql。同样用策略模式将这两中模式的搜索功能抽象成一个接口,分别实现搜索策略,然后定义环境角色根据配置选择具体的策略搜索文章。

需要的搜索功能是:根据输入的单词,在文章的标题和内容中找到相应的位置并高亮显示。

Es搜索逻辑

首先构造搜索条件:

		// 查询条件
		NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.boolQuery().should(QueryBuilders.matchQuery("articleTitle", keywords))
                        .should(QueryBuilders.matchQuery("articleContent", keywords)))
                .must(QueryBuilders.termQuery("isDelete", FALSE))
                .must(QueryBuilders.termQuery("status", PUBLIC.getStatus()));
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
		// 高亮显示条件 在关键词前后加上PRE_TAG POST_TAG
		HighlightBuilder.Field titleField = new HighlightBuilder.Field("articleTitle");
        titleField.preTags(PRE_TAG);
        titleField.postTags(POST_TAG);
        HighlightBuilder.Field contentField = new HighlightBuilder.Field("articleContent");
        contentField.preTags(PRE_TAG);
        contentField.postTags(POST_TAG);
        contentField.fragmentSize(50);
        nativeSearchQueryBuilder.withHighlightFields(titleField, contentField);

使用elasticsearchRestTemplate根据搜索条件查询

SearchHits<ArticleSearchDTO> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), ArticleSearchDTO.class);

将结果转为List返回

return search.getSearchHits().stream().map(hit -> {
                ArticleSearchDTO article = hit.getContent();
                List<String> titleHighLightList = hit.getHighlightFields().get("articleTitle");
                if (CollectionUtils.isNotEmpty(titleHighLightList)) {
                    article.setArticleTitle(titleHighLightList.get(0));
                }
                List<String> contentHighLightList = hit.getHighlightFields().get("articleContent");
                if (CollectionUtils.isNotEmpty(contentHighLightList)) {
                    article.setArticleContent(contentHighLightList.get(contentHighLightList.size() - 1));
                }
                return article;
            }).collect(Collectors.toList());
Mysql搜索逻辑

构造搜索条件 && 搜索

List<Article> articles = articleMapper.selectList(new LambdaQueryWrapper<Article>()
                .eq(Article::getIsDelete, FALSE)
                .eq(Article::getStatus, PUBLIC.getStatus())
                .and(i -> i.like(Article::getArticleTitle, keywords)
                        .or()
                        .like(Article::getArticleContent, keywords)));

对搜索结果高亮处理截取后返回

return articles.stream().map(item -> {
                    boolean isLowerCase = true;
                    String articleContent = item.getArticleContent();
                    int contentIndex = item.getArticleContent().indexOf(keywords.toLowerCase());
                    if (contentIndex == -1) {
                        contentIndex = item.getArticleContent().indexOf(keywords.toUpperCase());
                        if (contentIndex != -1) {
                            isLowerCase = false;
                        }
                    }
                    if (contentIndex != -1) {
                        int preIndex = contentIndex > 15 ? contentIndex - 15 : 0;
                        String preText = item.getArticleContent().substring(preIndex, contentIndex);
                        int last = contentIndex + keywords.length();
                        int postLength = item.getArticleContent().length() - last;
                        int postIndex = postLength > 35 ? last + 35 : last + postLength;
                        String postText = item.getArticleContent().substring(contentIndex, postIndex);
                        if (isLowerCase) {
                            articleContent = (preText + postText).replaceAll(keywords.toLowerCase(), PRE_TAG + keywords.toLowerCase() + POST_TAG);
                        } else {
                            articleContent = (preText + postText).replaceAll(keywords.toUpperCase(), PRE_TAG + keywords.toUpperCase() + POST_TAG);
                        }
                    } else {
                        return null;
                    }
                    isLowerCase = true;
                    int titleIndex = item.getArticleTitle().indexOf(keywords.toLowerCase());
                    if (titleIndex == -1) {
                        titleIndex = item.getArticleTitle().indexOf(keywords.toUpperCase());
                        if (titleIndex != -1) {
                            isLowerCase = false;
                        }
                    }
                    String articleTitle;
                    if (isLowerCase) {
                        articleTitle = item.getArticleTitle().replaceAll(keywords.toLowerCase(), PRE_TAG + keywords.toLowerCase() + POST_TAG);
                    } else {
                        articleTitle = item.getArticleTitle().replaceAll(keywords.toUpperCase(), PRE_TAG + keywords.toUpperCase() + POST_TAG);
                    }
                    return ArticleSearchDTO.builder()
                            .id(item.getId())
                            .articleTitle(articleTitle)
                            .articleContent(articleContent)
                            .build();
                }).filter(Objects::nonNull)
                .collect(Collectors.toList());
Tips

Es搜索为什么会比Mysql快呢?

  1. 基于分词后的全文检索,对于模糊搜索Mysql的索引会失效,而es会使用单词字典树结合倒排索引能够快速找到文章位置
  2. 进行精确检索,有些时候可能mysql要快一些,当mysql的非聚合索引引用上了聚合索引,无需回表,则速度上可能更快;es还是通过FST找到倒排索引的位置比获取文档id列表,再根据文档id获取文档并根据相关度进行排序。但是es还有个优势,就是es即天然的分布式能够在大量数据搜索时可以通过分片降低检索规模,并且可以通过并行检索提升效率,用filter时,更是可以直接跳过检索直接走缓存。

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

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

相关文章

批量对比文件夹下文件

软件 产品 | Beyond Compare 中文官方网站 软解破解 Beyond Compare 4密钥过期&#xff0c;解决办法&#xff0c;超实用 批量文件对比 最后显示红色的文件&#xff0c;即为文件两者内容不同的

面了10家却收不到1个offer,自我介绍你踩雷了吗?

每一次离职&#xff0c;都是为了重新开始&#xff1b;每一次寻找新的工作&#xff0c;都面临着巨大的竞争。找工作就像找对象&#xff0c;除了需要一定的缘分外&#xff0c;也需要掌握一定的技巧。 有的人爱恋TA的人排到长城&#xff0c;有的人却一直母胎单身&#xff1b;有的人…

Spring源码解析(十一):spring事务配置类源码

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a;bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三)&#xff1a;bean容器的刷新 Spring源码解析(四)&#xff1a;单例bean的创建流程 Spring源码解析(五)&…

vue3使用echarts实现地图撒点、飞线等功能

echarts地图配置参考链接 链接2 vue3使用echarts map.vue <template><div class"echart-demo" id"demo"></div> </template><script setup lang"ts"> //引入echart和json数据 import * as echarts from echarts…

ffmpeg ts 关于av_seek_frame

1 ffmpeg命令行 一般对视频文件的裁剪 我们通过一行 ffmpeg命令行即可实现&#xff0c;比如 ffmpeg -ss 0.5 - t 3 - i a.mp4 vcodec copy b.mp4 其中 -ss 放置较前 开启精准seek定位 对于mp4而言 seek将从moov中相关索引表查找 0.5s时刻附近最近的关键帧 &#xff08;此描述…

systemd服务日志重定向到文件

方式一&#xff08;centos7&#xff09; [Service] ExecStart"/usr/bin/sh test.sh >> info.log 2>&1"方式二&#xff08;centos8&#xff09; StandardOutput 和 StandardError&#xff0c;用于重定向标准输出和标准错误输出 [Service] StandardOut…

k8s containerd查看镜像

直接查看crictl image会报错&#xff1a; 1) crictl config runtime-endpoint unix:///run/containerd/containerd.sock 2) vi /etc/crictl.yaml 3) systemctl daemon-reload 此时&#xff0c;再查看image:

Kettle连接数据库[MySQL]报错

在连接数据库页面填写完成后点击“测试” 报错信息&#xff1a; 错误连接数据库 [ETLqiangzi] : org.pentaho.di.core.exception.KettleDatabaseException: Error occurred while trying to connect to the databaseDriver class org.gjt.mm.mysql.Driver could not be found…

nssm nginx window 部署和开机启动服务

部署 去到Nginx官网&#xff1a;nginx news &#xff0c;然后点击“download” 在nginx的配置文件是conf目录下的nginx.conf nginx.exe http://localhost 在cmd命令窗口里面输入nginx命令(快速停止nginx) &#xff1a; nginx -s stop 或者使用(完整有序的停止nginx)命…

elasticsearch深度分页问题

一、深度分页方式from size es 默认采用的分页方式是 from size 的形式&#xff0c;在深度分页的情况下&#xff0c;这种使用方式效率是非常低的&#xff0c;比如我们执行如下查询 1 GET /student/student/_search 2 { 3 "query":{ 4 "match_all":…

航拍飞行器经营商城小程序的作用是什么

航拍人群越来越越多&#xff0c;一款靠谱的装备往往能达到预期效果&#xff0c;随着互联网信息传播度加深&#xff0c;也吸引了大批同样的爱好者加入航拍序列。 对航拍飞行器企业/经营商来说&#xff0c;市场增幅下也带来了不少商机&#xff0c;然在实际销售及客户赋能方面还是…

必备的常见芯片封装

-网友&#xff1a;这什么破封装&#xff0c;这么难焊&#xff01; -工程师&#xff1a;你才焊过几种芯片封装呀&#xff0c;SOT封装都觉得难&#xff1f; 我们常见的芯片封装&#xff1a; 第一种&#xff0c;DIP封装&#xff0c;DIP即双列直插式封装&#xff0c;引脚从芯片两…

vue-2

一、文章内容概括 1.指令补充 指令修饰符v-bind对样式增强的操作v-model应用于其他表单元素 2.computed计算属性 基础语法计算属性vs方法计算属性的完整写法成绩案例 3.watch侦听器 基础写法完整写法 4.综合案例 &#xff08;演示&#xff09; 渲染 / 删除 / 修改数量 …

tomcat安装,创建web后端项目,部署项目过程

1&#xff0c;安装服务器&#xff0c;使用 Apache免费提供的服务器TomCat&#xff0c;注意JDK版本。 TomCat官方站点 文件解压目录。 启动服务器&#xff1a;bin目录下点击startup.bat&#xff0c;出现小黑框&#xff0c;浏览器默认访问http://127.0.0.1:8080/ 关闭服务器&…

C# 通过winmm枚举音频设备

文章目录 前言一、如何实现&#xff1f;1、DllImport接口&#xff08;1&#xff09;、方法&#xff08;2&#xff09;、结构体2、定义实体3、实现枚举 二、完整代码三、使用示例总结 前言 使用C#做音频录制时需要获取音频设备信息&#xff0c;比如使用ffmpeg进行录制需要先获取…

PMP该如何备考?

我觉得最主要的就是需要打造属于自己的学习计划&#xff0c;因为每个人的学习能力是不一样的&#xff0c;没有好的学习方法&#xff0c;就会导致学习不好&#xff0c;最终获不得成绩&#xff0c;拿不下证书。 所以接下来就说一下我自己的一些学习方法&#xff0c;如对你有用的…

家政小程序开发|家政预约维修保洁系统搭建

家政预约小程序开发&#xff0c;简单易用家政服务公司小程序&#xff0c;客户&#xff0b;员工&#xff0b;派单&#xff0b;合同&#xff0b;财务&#xff0b;营销获客一键搞定&#xff01; 那么家政小程序都有什么功能&#xff0c;今天我就给大家介绍下&#xff1b; 1、地理…

自学(黑客)技术方法 必看 ——网络安全

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

智能视频监控,究竟“智”在哪里?

当人们一提到智能视频监控时&#xff0c;就会想起高清摄像头、人脸识别等技术。其实不然&#xff0c;真正智能视频监控不仅仅是这些技术算法&#xff0c;更重要的是如何将这些算法融入到应用场景中&#xff0c;更好地去服务大众、起到降本增效的作用。 首先&#xff0c;智能视…

Nginx支持SNI证书,已经ssl_server_name的使用

整理了一些网上的资料&#xff0c;这里记录一下&#xff0c;供大家参考 什么是SNI&#xff1f; 传统的应用场景中&#xff0c;一台服务器对应一个IP地址&#xff0c;一个域名&#xff0c;使用一张包含了域名信息的证书。随着云计算技术的普及&#xff0c;在云中的虚拟机有了一…