2024050802-重学 Java 设计模式《实战模板模式》
重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」
一、前言
黎明前的坚守,的住吗?
有人举过这样一个例子,先给你张北大的录取通知书,但要求你每天5点起床,12点睡觉😪,刻苦学习,勤奋上进。只要你坚持三年,这张通知书就有效。如果是你,你能坚持吗?其实对于这个例子很难在我们的人生中出现,因为它目标明确,有准确的行军路线。就像你是土豪家庭,家里给你安排的明明白白一样,只要你按照这个方式走就不会有问题。可大多数时候我们并没有这样的路线,甚至不知道多久到达自己的黎明。但!谁又不渴望见到黎明呢,坚持吧!
不要轻易被洗脑
键盘侠⌨、网络喷壶,几乎当你努力坚持一件事的时候,在这条路上会遇到形形色色的人和事。有时候接收建议完善自己是有必要的,但不能放弃自己的初心和底线,有时候只坚持自己也是难能可贵的。子路之勇,子贡之辩,冉有之智,此三子者,皆天下之所谓难能而可贵者也。阳光和努力是这个世界最温暖的东西,加油坚持好自己的选的路。
有时还好坚持了
当你为自己的一个决定而感到万分开心😄时,是不是也非常感谢自己还好坚持了。坚持、努力、终身学习,似乎在程序员这个行业是离不开的,当你意愿于把这当做一份可以努力的爱好时,你就会愿意为此而努力。而我们很难说只在机会要来时准备,而是一直努力等待机会。也就是很多人说的别人抓住机会是因为一直在准备着。
二、开发环境
- JDK 1.8
- Idea + Maven
- 涉及工程三个,可以通过关注公众号:
bugstack虫洞栈,回复源码下载获取(打开获取的链接,找到序号18)
| 工程 | 描述 |
|---|---|
| itstack-demo-design-21-00 | 场景模拟工程;模拟爬虫商品生成海报场景 |
三、模版模式介绍

- 图片来自:https://refactoringguru.cn/design-patterns/template-method
模板模式的核心设计思路是通过在,抽象类中定义抽象方法的执行顺序,并将抽象方法设定为只有子类实现,但不设计独立访问的方法。简单说也就是把你安排的明明白白的。

就像西游记的99八十一难,基本每一关都是;师傅被掳走、打妖怪、妖怪被收走,具体什么妖怪你自己定义,怎么打你想办法,最后收走还是弄死看你本事,我只定义执行顺序和基本策略,具体的每一难由观音来安排。
四、案例场景模拟

在本案例中我们模拟爬虫各类电商商品,生成营销推广海报场景
关于模版模式的核心点在于由抽象类定义抽象方法执行策略,也就是说父类规定了好一系列的执行标准,这些标准的串联成一整套业务流程。
在这个场景中我们模拟爬虫爬取各类商家的商品信息,生成推广海报(海报中含带个人的邀请码)赚取商品返利。声明,这里是模拟爬取,并没有真的爬取
而整个的爬取过程分为;模拟登录、爬取信息、生成海报,这三个步骤,另外;
- 因为有些商品只有登录后才可以爬取,并且登录可以看到一些特定的价格这与未登录用户看到的价格不同。
- 不同的电商网站爬取方式不同,解析方式也不同,因此可以作为每一个实现类中的特定实现。
- 生成海报的步骤基本一样,但会有特定的商品来源标识。所以这样三个步骤可以使用模版模式来设定,并有具体的场景做子类实现。
五、模版模式搭建工程
模版模式的业务场景可能在平时的开发中并不是很多,主要因为这个设计模式会在抽象类中定义逻辑行为的执行顺序。一般情况下,我们用的抽象类定义的逻辑行为都比较轻量级或者没有,只是提供一些基本方法公共调用和实现。
但如果遇到适合的场景使用这样的设计模式也是非常方便的,因为他可以控制整套逻辑的执行顺序和统一的输入、输出,而对于实现方只需要关心好自己的业务逻辑即可。
而在我们这个场景中,只需要记住这三步的实现即可;模拟登录、爬取信息、生成海报
1. 工程结构
itstack-demo-design-21-00
└── src├── main│ └── java│ └── org.itstack.demo.design│ ├── group│ │ ├── DangDangNetMall.java│ │ ├── JDNetMall.java│ │ └── TaoBaoNetMall.java│ ├── HttpClient.java│ └── NetMall.java└── test└── java└── org.itstack.demo.design.test└── ApiTest.java
模版模式模型结构

- 以上的代码结构还是比较简单的,一个定义了抽象方法执行顺序的核心抽象类,以及三个模拟具体的实现(
京东、淘宝、当当)的电商服务。
2. 代码实现
2.1 定义执行顺序的抽象类
/*** 基础电商推广服务* 1. 生成最优价商品海报* 2. 海报含带推广邀请码*/
public abstract class NetMall {protected Logger logger = LoggerFactory.getLogger(NetMall.class);String uId; // 用户IDString uPwd; // 用户密码public NetMall(String uId, String uPwd) {this.uId = uId;this.uPwd = uPwd;}/*** 生成商品推广海报** @param skuUrl 商品地址(京东、淘宝、当当)* @return 海报图片base64位信息*/public String generateGoodsPoster(String skuUrl) {if (!login(uId, uPwd)) return null; // 1. 验证登录Map<String, String> reptile = reptile(skuUrl); // 2. 爬虫商品return createBase64(reptile); // 3. 组装海报}// 模拟登录protected abstract Boolean login(String uId, String uPwd);// 爬虫提取商品信息(登录后的优惠价格)protected abstract Map<String, String> reptile(String skuUrl);// 生成商品海报信息protected abstract String createBase64(Map<String, String> goodsInfo);}
- 这个类是此设计模式的灵魂
- 定义可被外部访问的方法
generateGoodsPoster,用于生成商品推广海报 generateGoodsPoster在方法中定义抽象方法的执行顺序 1 2 3 步- 提供三个具体的抽象方法,让外部继承方实现;模拟登录(
login)、模拟爬取(reptile)、生成海报(createBase64)
2.2 模拟爬虫京东
public class JDNetMall extends NetMall {public JDNetMall(String uId, String uPwd) {super(uId, uPwd);}public Boolean login(String uId, String uPwd) {logger.info("模拟京东用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}public Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<String, String>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "5999.00");logger.info("模拟京东商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}public String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成京东商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}}
- 模拟登录
- 爬取信息,这里只是把
title的信息爬取后的结果截取出来。 - 模拟创建
base64图片的方法
2.3 模拟爬虫淘宝
public class TaoBaoNetMall extends NetMall {public TaoBaoNetMall(String uId, String uPwd) {super(uId, uPwd);}@Overridepublic Boolean login(String uId, String uPwd) {logger.info("模拟淘宝用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}@Overridepublic Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<String, String>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "4799.00");logger.info("模拟淘宝商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}@Overridepublic String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成淘宝商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}}
- 同上,模拟登录和爬取以及创建图片的
base64
2.4 模拟爬虫当当
public class DangDangNetMall extends NetMall {public DangDangNetMall(String uId, String uPwd) {super(uId, uPwd);}@Overridepublic Boolean login(String uId, String uPwd) {logger.info("模拟当当用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}@Overridepublic Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<String, String>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "4548.00");logger.info("模拟当当商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}@Overridepublic String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成当当商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}}
- 同上,模拟登录和爬取以及创建图片的
base64
3. 测试验证
3.1 编写测试类
/*** 测试链接* 京东;https://item.jd.com/100008348542.html* 淘宝;https://detail.tmall.com/item.htm* 当当;http://product.dangdang.com/1509704171.html*/
@Test
public void test_NetMall() {NetMall netMall = new JDNetMall("1000001","*******");String base64 = netMall.generateGoodsPoster("https://item.jd.com/100008348542.html");logger.info("测试结果:{}", base64);
}
- 测试类提供了三个商品链接,也可以是其他商品的链接
- 爬取的过程模拟爬取京东商品,可以替换为其他商品服务
new JDNetMall、new TaoBaoNetMall、new DangDangNetMall
3.2 测试结果
23:33:13.616 [main] INFO org.itstack.demo.design.NetMall - 模拟京东用户登录 uId:1000001 uPwd:*******
23:33:15.038 [main] INFO org.itstack.demo.design.NetMall - 模拟京东商品爬虫解析:【AppleiPhone 11】Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待【行情 报价 价格 评测】-京东 | 5999.00 元 https://item.jd.com/100008348542.html
23:33:15.038 [main] INFO org.itstack.demo.design.NetMall - 模拟生成京东商品base64海报
23:33:15.086 [main] INFO org.itstack.demo.design.test.ApiTest - 测试结果:eyJwcmljZSI6IjU5OTkuMDAiLCJuYW1lIjoi44CQQXBwbGVpUGhvbmUgMTHjgJFBcHBsZSBpUGhv
bmUgMTEgKEEyMjIzKSAxMjhHQiDpu5HoibIg56e75Yqo6IGU6YCa55S15L+hNEfmiYvmnLog5Y+M
5Y2h5Y+M5b6F44CQ6KGM5oOFIOaKpeS7tyDku7fmoLwg6K+E5rWL44CRLeS6rOS4nCJ9Process finished with exit code 0
六、总结
- 通过上面的实现可以看到
模版模式在定义统一结构也就是执行标准上非常方便,也就很好的控制了后续的实现者不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体的业务逻辑实现即可。 - 另外模版模式也是为了解决子类通用方法,放到父类中设计的优化。让每一个子类只做子类需要完成的内容,而不需要关心其他逻辑。这样提取公用代码,行为由父类管理,扩展可变部分,也就非常有利于开发拓展和迭代。
- 但每一种设计模式都有自己的特定场景,如果超过场景外的建设就需要额外考虑🤔其他模式的运用。而不是非要生搬硬套,否则自己不清楚为什么这么做,也很难让后续者继续维护代码。而想要活学活用就需要多加练习,有实践的经历。
相关文章:
2024050802-重学 Java 设计模式《实战模板模式》
重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」 一、前言 黎明前的坚守,的住吗? 有人举过这样一个例子,先给你张北大的录取通知书,但要求你每天5点起床,12点…...
UNIAPP-ADB无线调试
ADB下载 SDK 平台工具版本说明 | Android Studio | Android Developers (google.cn) 环境变量配置 ADB版本查看 adb version 手机使用数据线连接到电脑 手机需要授权adb调试(开发人员选项里面) CMD输入命令 adb tcpip 5555 到了这一步你手机已经启动了adb服务了&…...
【stm32-新建工程】
stm32-新建工程 ■ 下载相关STM32Cube官方固件包(F1,F4,F7,H7)■ 1. ST官方搜索STM32Cube■ 2. 搜索 STM32Cube■ 3. 点击获取软件■ 4. 选择对应的版本下载■ 5. 输入账号信息■ 6. 出现下载弹框,等待下载…...
写点什么吧,作为STM32系列的开篇……
自从本科毕业后,就再也没碰过单片机…… 自从研究生毕业后,就再也没碰过硬件…… 自以为以前单片机玩的熟得很,特别是ATMEGA系列的AVR单片机,由于老师的推荐,本科时花了好多精力在这个系列单片机上面…… 本科时STM…...
代码随想录算法训练营第十天| 232.用栈实现队列|225. 用队列实现栈|20. 有效的括号|1047. 删除字符串中的所有相邻重复项
232.用栈实现队列 文档讲解:代码随想录 视频讲解:栈的基本操作! | LeetCode:232.用栈实现队列_哔哩哔哩_bilibili 知道要用两个栈实现,具体咋做忘了。队列的特性是先进先出,栈是先进后出,入队操…...
Pulsar 社区周报 | No.2024-06-07 | Apache Pulsar 新分支 3.3 版本发布
“ 各位热爱 Pulsar 的小伙伴们,Pulsar 社区周报更新啦!这里将记录 Pulsar 社区每周的重要更新,每周发布。 ” 本期主题:Apache Pulsar 新分支 3.3 版本发布 Apache Pulsar 新分支 3.3 版本发布:Apache Pulsar 3.3.0[1…...
Go源码--sync库(3):sync.Pool(2)
回收 回收其实就是将 pool.local 置为空 可以让垃圾回收器回收 我们来看下 源码 func init() {// 将 poolCleanup 注册到 gc开始前的准备工作处理器中在 STW时执行runtime_registerPoolCleanup(poolCleanup) }这里注册了清理程序到GC前准备工作 也就是发生GC前需要执行这段代…...
Go如何在本地引用以及发布并引用自定义工具包
如何引用本地自定义工具包 我们首先要准备两个项目,分别为需要引入的工具包和当前项目。 myutils、myproject1. myutils为我们的项目1-工具包 package mypakgeimport "strings"func IsContains(s string) bool {if strings.Contains(s, "a")…...
使用了代理IP怎么还会被封?代理IP到底有没有效果
代理IP作为一种网络工具,被广泛应用于各种场景,例如网络爬虫、海外购物、规避地区限制等。然而,很多用户在使用代理IP的过程中却发现自己的账号被封禁,这让他们不禁产生疑问:使用了代理IP怎么还会被封?代理…...
在WSL2的Ubuntu中安装和使用Docker/Podman
在WSL2的Ubuntu中安装和使用Docker/Podman 0. 目的 当网络环境良好(例如在公司,能直接访问Google等)时, Docker/Podman 安装和使用不是问题。 当网络环境不佳(例如在家里),要把 WSL2 的 Ubun…...
【WEEK16】Learning Objectives and Summaries【Spring Boot】【English Version】
Learning Objectives: Learning SpringBoot Learning Content: Reference video tutorials【狂神说Java】SpringBoot最新教程IDEA版通俗易懂Dubbo and Zookeeper Integration Learning time and outputs: Week16 TUE~FRI 2024.6.11【WEEK16】 【DAY2】Dubbo和Zookeeper集成第…...
AI大模型会让搜索引擎成为历史吗?
AI大模型会让搜索引擎成为历史吗? 随着人工智能技术的不断发展,AI大模型已经在许多领域展现出了强大的能力。从自然语言处理到图像识别,AI大模型的应用越来越广泛。在这种背景下,有人开始提出一个问题:AI大模型是否可…...
SpringSecurity6从入门到实战之SpringSecurity6自定义认证规则
SpringSecurity6从入门到实战之SpringSecurity6自定义认证规则 Spring Security 中默认所有的 http 请求都需要先认证通过后,才能访问。那么, 如何指定不需要认证就可以直接访问的资源呢?比如 用户的登录页面和注册页面,都是不需要…...
Java IO:byte[]、char[]、String三种对象的转换
String与byte[]对象进行转换时应指定编码格式,否则有潜在的乱码问题。byte[] b s.getBytes(“utf-8”); String s new String(b,“utf-8”); Java的IO库提供了专门的管道来对这3个对象进行读写,他们是StringReader/Writer CharArrayReader/Writer Byt…...
Elasticsearch:简化数据流的数据生命周期管理
作者:来自 Elastic Andrei Dan 今天,我们将探索 Elasticsearch 针对数据流的新数据管理系统:数据流生命周期,从版本 8.14 开始提供。凭借其简单而强大的执行模型,数据流生命周期可让n 你专注于数据生命周期的业务相关方…...
Verilog综合出来的图
Verilog写代码时需要清楚自己综合出来的是组合逻辑、锁存器还是寄存器。 甚至,有时写的代码有误,vivado不能识别出来,这时打开综合后的schematic简单查看一下是否综合出想要的结果。 比如:误将一个always模块重复一遍,…...
KT-H6测距模块标品,测距范围1500m,demo报价1000RMB,批量报价500RMB
激光测距传感器是一种用于测量距离的模块,通常由传感器和相关电子设备组成,测距模块可以集成到各种设备和系统中,以实现准确的测距和定位功能。KT-H6系列激光测距模块,为自主研发,激光波长905nm的激光器,专为热成像、夜视仪、无人机、安防、瞄具等产品定身打造,其优点是…...
C数据结构:排序
目录 冒泡排序 选择排序 堆排序 插入排序 希尔排序 快速排序 hoare版本 挖坑法 前后指针法 快速排序优化 三数取中法 小区间优化 快速排序非递归 栈版本 队列版本 归并排序 归并排序非递归 编辑 计数排序 各排序时间、空间、稳定汇总 冒泡排序 void Bub…...
【Python】在 Pandas 中使用 AdaBoost 进行分类
我们都找到天使了 说好了 心事不能偷藏着 什么都 一起做 幸福得 没话说 把坏脾气变成了好沟通 我们都找到天使了 约好了 负责对方的快乐 阳光下 的山坡 你素描 的以后 怎么抄袭我脑袋 想的 🎵 薛凯琪《找到天使了》 在数据科学和机器学习的工作…...
持续总结中!2024年面试必问 20 道并发编程面试题(九)
上一篇地址:持续总结中!2024年面试必问 20 道并发编程面试题(八)-CSDN博客 十七、请解释什么是Callable和FutureTask。 Callable和FutureTask是Java并发API中的重要组成部分,它们用于处理可能产生结果的异步任务。 …...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
