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中的重要组成部分,它们用于处理可能产生结果的异步任务。 …...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
