Java设计模式:四、行为型模式-09:模板模式
文章目录
- 一、定义:模板模式
- 二、模拟场景:模板模式
- 三、改善代码:模板模式
- 3.0 引入依赖
- 3.1 工程结构
- 3.2 模板模式结构图
- 3.3 爬取商品生成海报实现
- 3.3.1 HTTP获取连接类
- 3.3.2 定义执行顺序的抽象类
- 3.3.3 当当爬取抽象实现类
- 3.3.4 京东爬取抽象实现类
- 3.3.5 淘宝爬取抽象实现类
- 3.4 单元测试
- 四、总结:模板模式
一、定义:模板模式

- 模板模式:通过在抽象类中定义抽象方法的执行顺序,并将抽象方法设定为只有子类实现,但不涉及
独立访问的方法。
二、模拟场景:模板模式

- 模拟爬虫各类电商商品,生成营销推广海报场景。
- 模板模式的核心点在于:
- 由抽象类定义抽象方法执行策略,也就是说父类规定好了 一系列的执行标准,这些标准串联成一整套业务流程。
- 在这个场景中模拟爬虫爬取各类商家的商品信息,生成推广海报,赚取商品返利。
- 整个爬取过程分为三个步骤:模拟登录、爬取信息、生成海报。
- 因为有些商品只有登录后才可以爬取,并且登录可以看到一些特定的价格,这与未登录用户看到的价格不同。
- 不同的电商网站爬取方式不同,解析方式也不同,因此可以作为每一个实现类中的特定实现。
- 生成海报的步骤基本一样,但会有特定的商品来源标识。所以这三个步骤可以使用模板模式来设定,并有具体的场景做子类实现。
三、改善代码:模板模式
💡 模板模式的业务场景可能在世的开发中并不是很多,主要因为这个设计模式会在抽象类中定义逻辑行为的执行顺序。
一般情况下,我们用的抽象类定义的逻辑行为都比较轻量级或者没有,只有提供一些基本方法公共调用和实现。
- 但如果遇到适合的场景使用这样的设计模式也是非常方便的,因为他可以控制整套逻辑的执行顺序和统一的输入、输出,而对于实现方只需要关心好自己的业务逻辑即可。
- 在模拟场景中,只需要记住三步实现:
模拟登录、爬取信息、生成海报。
3.0 引入依赖
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!-- LOGGING begin --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.5</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>jcl-over-slf4j</artifactId><version>1.7.5</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.0.9</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>
</dependencies>
3.1 工程结构
design-step-22
|——src|——main|--java|--com.lino.design|--impl| |--DangDangNetMall.java| |--JDNetMall.java| |--TaoBaoNetMall.java|-HttpClient.java|-NetMall.java|--test|--com.lino.design.test|-ApiTest.java
3.2 模板模式结构图

- 一个定义了抽象方法执行顺序的核心抽象类,以及三个模拟具体的实现(
京东、淘宝、当当)的电商服务。
3.3 爬取商品生成海报实现
3.3.1 HTTP获取连接类
HttpClient.java
package com.lino.design;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;/*** @description: http请求类*/
public class HttpClient {public static String doGet(String httpUrl) {HttpURLConnection connection = null;InputStream is = null;BufferedReader br = null;String result = null;try {// 创建远程url连接对象URL url = new URL(httpUrl);// 通过远程url连接对象打开一个连接,强转成HttpURLConnectionconnection = (HttpURLConnection) url.openConnection();// 设置连接方式:getconnection.setRequestMethod("GET");// 设置连接主机服务器的超时时间:15000毫秒connection.setConnectTimeout(15000);// 设置读取远程返回的数据时间:60000毫秒connection.setReadTimeout(60000);// 发送请求connection.connect();// 通过connection连接,获取输入流if (connection.getResponseCode() == 200) {is = connection.getInputStream();// 封装输入流is,并指定字符集br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));// 存放数据StringBuilder sbf = new StringBuilder();String temp = null;while ((temp = br.readLine()) != null) {sbf.append(temp);sbf.append("\r\n");}result = sbf.toString();}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源if (null != br) {try {br.close();} catch (IOException e) {e.printStackTrace();}}if (null != is) {try {is.close();} catch (IOException e) {e.printStackTrace();}}// 关闭远程连接assert connection != null;connection.disconnect();}return result;}
}
3.3.2 定义执行顺序的抽象类
NetMall.java
package com.lino.design;import com.sun.org.apache.xpath.internal.operations.Bool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.regex.Pattern;/*** @description: 抽象模板*/
public abstract class NetMall {protected Logger logger = LoggerFactory.getLogger(NetMall.class);protected final Pattern p9 = Pattern.compile("(?<=title\\>).*(?=</title)");/*** 用户ID*/String uId;/*** 用户密码*/String uPwd;public NetMall(String uId, String uPwd) {this.uId = uId;this.uPwd = uPwd;}/*** 生成商品推广海报** @param skuUrl 商品url地址* @return 推广海报*/public String generateGoodsPoster(String skuUrl) {// 1.验证登录if (!login(uId, uPwd)) {return null;}// 2.爬虫商品Map<String, String> reptile = reptile(skuUrl);// 3.组装海报return createBase64(reptile);}/*** 模拟登录** @param uId 用户ID* @param uPwd 用户密码* @return 登录结果*/protected abstract Boolean login(String uId, String uPwd);/*** 爬虫提取商品信息(登录后的优惠价格)** @param skuUrl 商品Url地址* @return 商品信息*/protected abstract Map<String, String> reptile(String skuUrl);/*** 生成商品推广海报** @param goodsInfo 商品信息* @return 推广海报*/protected abstract String createBase64(Map<String, String> goodsInfo);
}
- 这个类是模板模式的灵魂。
- 定义可外被外部访问的方法
generateGoodsPoster,用于生成商品推广海报。 generateGoodsPoster在方法中定义抽象方法的执行顺序1、2、3步。- 提供三个具体的抽象方法,让外部继承实现。
login:模拟登录。reptile:爬虫提取商品信息(登录后的优惠价格)。createBase64:生成商品推广海报。
3.3.3 当当爬取抽象实现类
DangDangNetMall.java
package com.lino.design.impl;import com.alibaba.fastjson.JSON;
import com.lino.design.HttpClient;
import com.lino.design.NetMall;
import sun.misc.BASE64Encoder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @description: 当当抽象实现类*/
public class DangDangNetMall extends NetMall {public DangDangNetMall(String uId, String uPwd) {super(uId, uPwd);}@Overrideprotected Boolean login(String uId, String uPwd) {logger.info("模拟当当用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}@Overrideprotected Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "4548.00");logger.info("模拟当当商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}@Overrideprotected String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成当当商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}
}
3.3.4 京东爬取抽象实现类
JDNetMall.java
package com.lino.design.impl;import com.alibaba.fastjson.JSON;
import com.lino.design.HttpClient;
import com.lino.design.NetMall;
import sun.misc.BASE64Encoder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;/*** @description: 京东抽象实现类*/
public class JDNetMall extends NetMall {public JDNetMall(String uId, String uPwd) {super(uId, uPwd);}@Overrideprotected Boolean login(String uId, String uPwd) {logger.info("模拟京东用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}@Overrideprotected Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "5999.00");logger.info("模拟京东商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}@Overrideprotected String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成京东商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}
}
3.3.5 淘宝爬取抽象实现类
TaoBaoNetMall.java
package com.lino.design.impl;import com.alibaba.fastjson.JSON;
import com.lino.design.HttpClient;
import com.lino.design.NetMall;
import sun.misc.BASE64Encoder;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;/*** @description: 淘宝抽象实现类*/
public class TaoBaoNetMall extends NetMall {public TaoBaoNetMall(String uId, String uPwd) {super(uId, uPwd);}@Overrideprotected Boolean login(String uId, String uPwd) {logger.info("模拟淘宝用户登录 uId:{} uPwd:{}", uId, uPwd);return true;}@Overrideprotected Map<String, String> reptile(String skuUrl) {String str = HttpClient.doGet(skuUrl);Matcher m9 = p9.matcher(str);Map<String, String> map = new ConcurrentHashMap<>();if (m9.find()) {map.put("name", m9.group());}map.put("price", "4799.00");logger.info("模拟淘宝商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);return map;}@Overrideprotected String createBase64(Map<String, String> goodsInfo) {BASE64Encoder encoder = new BASE64Encoder();logger.info("模拟生成淘宝商品base64海报");return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());}
}
💡 模拟登录、爬取信息、生成海报由三个实现类分别实现。
3.4 单元测试
ApiTest.java
package com.lino.design.test;import com.lino.design.NetMall;
import com.lino.design.impl.JDNetMall;
import jdk.nashorn.internal.scripts.JD;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** @description: 单元测试*/
public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);private String JD_URL = "https://item.jd.com/100008348542.html";private String TAO_BAO_URL = "https://detail.tmall.com/item.htm";private String DANG_DANG_URL = "http://product.dangdang.com/1509704171.html";@Testpublic void test_NetMall() {NetMall netMall = new JDNetMall("100001", "******");String base64 = netMall.generateGoodsPoster(JD_URL);logger.info("测试结果:{}", base64);}
}
- 测试类提供了三个商品连接,也可以是其他商品的连接。
- 爬取的成功模拟爬取京东商品,可以替换为其他商品服务。
new JDNetMall、new DangDangNetMall、new TaoBaoNetMall
测试结果
10:36:08.491 [main] INFO com.lino.design.NetMall - 模拟京东用户登录 uId:100001 uPwd:******
10:36:09.582 [main] INFO com.lino.design.NetMall - 模拟京东商品爬虫解析:【AppleiPhone 11】Apple iPhone 11 (A2223) 128GB 黑色 移动联通电信4G手机 双卡双待【行情 报价 价格 评测】-京东 | 5999.00 元 https://item.jd.com/100008348542.html
10:36:09.582 [main] INFO com.lino.design.NetMall - 模拟生成京东商品base64海报
10:36:09.615 [main] INFO com.lino.design.test.ApiTest - 测试结果:eyJwcmljZSI6IjU5OTkuMDAiLCJuYW1lIjoi44CQQXBwbGVpUGhvbmUgMTHjgJFBcHBsZSBpUGhv
bmUgMTEgKEEyMjIzKSAxMjhHQiDpu5HoibIg56e75Yqo6IGU6YCa55S15L+hNEfmiYvmnLog5Y+M
5Y2h5Y+M5b6F44CQ6KGM5oOFIOaKpeS7tyDku7fmoLwg6K+E5rWL44CRLeS6rOS4nCJ9
四、总结:模板模式
- 通过上面的实现可以看到 模板模式 在定义统一结构也就是执行标准上非常方便。
- 也就很好的控制了后续的实现这不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体的业务逻辑实现即可。
- 模板模式也是对了解决子类通用方法,放到父类中设计的优化。让每一个子类只做子类需要完成的内容,而不需要关心其他逻辑。
- 这样提取公共代码,行为由父类管理,扩展可变部分,也就非常有利于开发拓展和迭代。
相关文章:
Java设计模式:四、行为型模式-09:模板模式
文章目录 一、定义:模板模式二、模拟场景:模板模式三、改善代码:模板模式3.0 引入依赖3.1 工程结构3.2 模板模式结构图3.3 爬取商品生成海报实现3.3.1 HTTP获取连接类3.3.2 定义执行顺序的抽象类3.3.3 当当爬取抽象实现类3.3.4 京东爬取抽象实…...
【前端】Vue2 脚手架模块化开发 -快速入门
🎄欢迎来到边境矢梦的csdn博文🎄 🎄本文主要梳理Vue2 脚手架模块化开发 🎄 🌈我是边境矢梦,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下🫰&#x…...
【广州华锐互动】AR昆虫认知学习系统实现对昆虫形态的捕捉和还原
随着科技的不断发展,人们对自然界的认识也在不断加深。在这个过程中,AR(增强现实)技术的出现为人们带来了全新的体验方式。为此,广州华锐互动开发了AR昆虫认知学习系统,本文将为大家详细介绍这款系统的特点…...
nginx压缩ttf文件 mine.types的作用
最近在运维过程中,前端提到发现在linux上下载某ttl文件(字体文件)太大,传输过程比较慢,于是就想着使用nginx的gzip进行压缩,经过不断尝试,终于发现在nginx的配置目录/etc/nginx/mine.types 文件…...
【云原生】Kubernetes容器编排工具
目录 1. K8S介绍 1.1 k8s的由来 下载地址 1.2 docker编排与k8s编排相比 1.3 传统后端部署与k8s 的对比 传统部署 k8s部署 2. k8s的集群架构与组件 (1) Kube-apiserver (2)Kube-controller-manager (3&a…...
【Css】Less和Sass的区别:
文章目录 一、定义:【1】Less【2】Sass 二、相同之处:三、区别:【1】实现方式:【2】实现方式:【3】混合(Mixins):【4】解析方式:【5】变量的作用域:【6】比起Less 一、定义: 【1】Less Less 是…...
八、MySQL(DML)如何修改表中的数据?
1、修改表数据 (1)基础语法: update 表名 SET 字段名1数值1,字段名2数值2,…… [where 条件]; (2) 操作实例: 第一步: 先准备一张表 insert into things values (10086,18,0x12…...
Python使用 YOLO_NAS_S 模型进行目标检测并保存预测到的主体图片
一、前言: 使用 YOLO_NAS_S 模型进行目标检测,并保存预测到的主体图片 安装包: pip install super_gradients pip install omegaconf pip install hydra-core pip install boto3 pip install stringcase pip install typing-extensions pi…...
<AIX>《AIX RAID 操作之LV逻辑卷镜像制作,即lvcopy操作》
《AIX RAID 操作之LV逻辑卷镜像制作,即lvcopy操作》 1 RAID技术2 AIX逻辑卷组做镜像3 环境3.1 操做系统版本3.2 检查rootvg的lv3.3 检查rootvg的磁盘信息4 创建测试的test的lv逻辑卷4.1 测试1:直接创建镜像lv4.2 测试2:创建未开启镜像的lv 5 …...
JSX底层渲染机制
JSX底层渲染机制 一,.步骤 1.把我们写的jsx语法编译为虚拟DOM【virtualDOM】 虚拟DOM对象:框架自己内部构建的一套对象体系(对象的相关成员都是React内部绑定的),基于这些属性描述出我们所构建视图中的DOM接的相关特征 1基于ba…...
2023_Spark_实验六:Scala面向对象部分演示(二)(IDEA开发)
7、Scala中的apply方法() 遇到如下形式的表达式时,apply方法就会被调用: Object(参数1,参数2,......,参数N) 通常,这样一个apply方法返回的是伴生类的对象;其作用是为了省略new关键字 Object的apply方法…...
ArcGIS美化科研论文地图(利用在线底图)
1.加载在线底图服务 在ArcGIS Desktop中打开Catalog窗口,在GIS Servers中点击Add ArcGIS Server,之后选项全部默认,仅在URL中输入以下网址https://services.arcgisonline.com/arcgis 之后就可以看到底图了 我们在WorldElevation3D中选择Nat…...
vue项目静态文件资源下载
业务场景:页面有一个导入功能,需要一个模板文件供下载,文件放在本地。 对于 Vue 3 Vite 项目,使用 require 方法来导入模块是不被支持的。require 是 CommonJS 规范中用于模块导入的方法,在 Webpack 等构建工具中常用…...
Apache Hudi初探(三)(与flink的结合)--flink写hudi的操作(真正的写数据)
背景 在之前的文章中Apache Hudi初探(二)(与flink的结合)–flink写hudi的操作(JobManager端的提交操作) 有说到写hudi数据会涉及到写hudi真实数据以及写hudi元数据,这篇文章来说一下具体的实现 写hudi真实数据 这里的操作就是在HoodieFlinkWriteClient.upsert方法: public …...
解释 Git 的基本概念和使用方式(InsCode AI 创作助手)
Git 是一种分布式版本控制系统,它允许多个用户协同工作并对项目进行版本控制。下面是 Git 的基本概念和使用方式: 基本概念: 仓库(Repository):存储代码和版本历史记录的地方。 提交(Commit&a…...
【QT】信号和槽(15)
前面的内容说了很多不同的控件如何使用,今天来看下QT的核心,信号与槽(Signals and slots)! 简单理解一下,就是我们的信号与槽连接上了之后,发射一个信号给到槽,槽函数接收到了这个信…...
EFLK日志平台(filebeat-->kafka-->logstash-->es-->kiabana)
ELK平台是一套完整的日志集中处理解决方案,将 ElasticSearch、Logstash 和 Kiabana 三个开源工具配合使用, 完成更强大的用户对日志的查询、排序、统计需求。 安装顺序 1.安装es 7.17.12 2.安装kibana 7.17.12 3.安装x-pack 保证以上调试成功后开始下面…...
C盘扩容遇到的问题(BitLocker解密、)
120G的C盘不知不觉的就满了,忍了好久终于要动手了。 尽管电脑-管理--磁盘管理里可以进行磁盘大小调整,但由于各盘都在用,不能够连续调整,所以选用DiskGenius。 # DiskGenius调整分区大小遇到“您选择的分区不支持无损调整容量” …...
ShardingSphere——柔性事务SEATA原理
摘要 Apache ShardingSphere集成了 SEATA 作为柔性事务的使用方案,本文主要介绍其实现ShardingSphere中柔性事务SEATA原理原理。帮助你更好的理解ShardingSphere原理。同时帮助大家更好的使用柔性事务SEATA原理。 一、Seata柔性事务 Apache ShardingSphere 集成了…...
Introducing GlobalPlatform(一篇了解GP)
安全之安全(security)博客目录导读 TEE之GP(Global Platform)认证汇总 目录 一、GP简介 二、GP新的重点领域是什么? 三、认证程序和培训<...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
