Java设计模式:四、行为型模式-09:模板模式

news2024/11/20 6:31:38

文章目录

  • 一、定义:模板模式
  • 二、模拟场景:模板模式
  • 三、改善代码:模板模式
    • 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连接对象打开一个连接,强转成HttpURLConnection
            connection = (HttpURLConnection) url.openConnection();
            // 设置连接方式:get
            connection.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);
    }

    @Override
    protected Boolean login(String uId, String uPwd) {
        logger.info("模拟当当用户登录 uId:{} uPwd:{}", uId, uPwd);
        return true;
    }

    @Override
    protected 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;
    }

    @Override
    protected 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);
    }

    @Override
    protected Boolean login(String uId, String uPwd) {
        logger.info("模拟京东用户登录 uId:{} uPwd:{}", uId, uPwd);
        return true;
    }

    @Override
    protected 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;
    }

    @Override
    protected 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);
    }

    @Override
    protected Boolean login(String uId, String uPwd) {
        logger.info("模拟淘宝用户登录 uId:{} uPwd:{}", uId, uPwd);
        return true;
    }

    @Override
    protected 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;
    }

    @Override
    protected 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";

    @Test
    public void test_NetMall() {
        NetMall netMall = new JDNetMall("100001", "******");
        String base64 = netMall.generateGoodsPoster(JD_URL);
        logger.info("测试结果:{}", base64);
    }
}
  • 测试类提供了三个商品连接,也可以是其他商品的连接。
  • 爬取的成功模拟爬取京东商品,可以替换为其他商品服务。new JDNetMallnew DangDangNetMallnew 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 11Apple 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

四、总结:模板模式

  • 通过上面的实现可以看到 模板模式 在定义统一结构也就是执行标准上非常方便。
    • 也就很好的控制了后续的实现这不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体的业务逻辑实现即可。
  • 模板模式也是对了解决子类通用方法,放到父类中设计的优化。让每一个子类只做子类需要完成的内容,而不需要关心其他逻辑。
    • 这样提取公共代码,行为由父类管理,扩展可变部分,也就非常有利于开发拓展和迭代。

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

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

相关文章

java八股文面试[多线程]——synchronized锁升级详细流程

偏向锁 偏向锁是JDK6中的重要引进&#xff0c;因为HotSpot作者经过研究实践发现&#xff0c;在大多数情况下&#xff0c;锁不仅不存在多线程竞争&#xff0c;而且总是由同一线程多次获得&#xff0c;为了让线程获得锁的代价更低&#xff0c;引进了偏向锁。 偏向锁是在单线程执…

python3.11教程1:python基础语法、程序控制、函数

文章目录 一、Python简介1.1 为什么学习python1.2 python安装与配置1.3 python解释器1.4 命令行参数1.4.1 sys.argv变量1.4.2 -c和-m选项 1.5 解释器的运行环境1.5.1 编码格式1.5.2 编码声明 二、Python基础语法2.1 行结构2.2 变量&#xff08;标识符&#xff09;2.3 字节串2.4…

如何让照片动起来?几步操作轻松动起来

现在&#xff0c;许多人都喜欢在社交媒体上分享自己的照片。但是&#xff0c;有时单张静态照片可能无法完全表达出你想要表达的感觉。为了使你的照片更生动有趣&#xff0c;你可以使用一些简单的技巧使它们动起来。下面是几个简单的步骤。 步骤1&#xff1a;打开制作应用并导入…

图像融合去雾、近红外去雾、(近)红外和可见光数据集

今天给大家分享一篇发表在IEEE TMM上的去雾文章Joint Contrast Enhancement and Exposure Fusion for Real-World Image Dehazing 作者从对比度增强和曝光融合的视角来解决图像去雾问题&#xff0c;在真实场景上取得了较好的去雾效果。此外&#xff0c;作者将所提出的方法应用…

Json解析流程

一、拿到了题库 分析一下可以定义的 1、序号&#xff0c;用来区分题目数&#xff0c;每个题有唯一的序号 2、题目&#xff0c;就是下图的Q 3、预设的回答&#xff0c;下图的A 分析完我可以知道有三个字段&#xff0c;分别是int index、string Q、string A。 二、把字段丢到…

MongoDB 会丢数据吗? 在次补刀MongoDB 双机热备

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis &#xff0c;Oracle ,Oceanbase 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请加微信号 liuaustin3 &#xff08;…

git 查看当前分支最近一次提交的commit SHA

获取当前分支最近一次commit SHA &#xff08;长度为40个16进制数字的字符&#xff09;命令如下&#xff1a; git rev-parse HEAD 获取简写&#xff08;短&#xff09; commit SHA git rev-parse --short HEAD

灾备中的网络加密是什么?

什么是网络加密&#xff1f; 在网络设计之初&#xff0c;对网络安全的问题考虑的不周全&#xff0c;数据在链路上传输的时候都是明文传输的&#xff0c;稍微有点技术的人&#xff0c;都可以轻松的拿到网络上的数据流量&#xff0c;并进行查看。试想一下&#xff0c;如果我们为…

Revit SDK 介绍:DistanceToPanels 根据距离设置参数

前言 这个例子展示如何计算距离&#xff0c;并将距离的值设置为参数。 内容 选中球形&#xff0c;运行程序&#xff0c;会设置控制高度的参数&#xff0c;距离越远参数值越大。效果如下所示&#xff1a; 核心逻辑&#xff1a; 得到选中物体的位置遍历分割表面内部的 Panel…

DAY07_Maven高级——分模块开发与设计依赖管理聚合与继承属性管理多环境配置与应用私服

目录 一 分模块开发与设计1. 分模块开发的意义问题导入模块拆分原则 2. 分模块开发问题导入2.1 创建Maven模块2.2 书写模块代码2.3 通过maven指令安装模块到本地仓库&#xff08;install指令&#xff09; 二 依赖管理1. 依赖传递问题导入 2. 可选依赖问题导入 3. 排除依赖问题导…

HVV行动之态势感知平台(一)

知攻善防&#xff0c;遇强则强&#xff01; 先介绍一下什么是HVV行动&#xff1a; 它是由公安部牵头的&#xff0c;通过组织红队和蓝队进行为期两周到三周的攻防对抗演习&#xff0c;来检测一些企业单位可能存在的网络漏洞和威胁&#xff0c;进而进行修复和加固&#xff0c;提…

go web之一:hello world快速上手+handle(http.Handle和http.HandleFunc的区别与联系)

前情提要&#xff1a; 需要安装好go的环境和VSCode的go插件。 hello world快速上手 1、创建go.mod 在项目根目录下打开命令行&#xff0c;或者直接用VSCode中的终端。输入命令 go mod init github.com/solenovex/web-tutorial 然后就能看到项目结构中多了一个go.mod 2、…

石油化工智慧安监方案:TSINGSEE青犀视频AI智能识别安全生产风险预警平台建设

一、行业背景 石油化工生产存储企业属于高温、高压、易燃、易爆、有毒的危险行业&#xff0c;其生产装置大型化、密集化、生产工艺复杂、生产过程紧密耦合。随着互联网技术的发展&#xff0c;运用先进的AI、物联网、大数据、云计算等技术手段不断提高石油化工行业的安全监管水…

postgis数据库导出csv表再导入postgis

1、导出csv表 from settings_Address import * from sqlalchemy import create_engine, MetaData import pandas as pd def create_conn(Postgis_user,Postgis_password,Postgis_host,Postgis_port,dbname_PG):# return create_engine(PostgispyPostgis://{}:{}{}:{}/{}.forma…

jmeter源码二次开发

本文以jmeter5.5为例&#xff0c;扩展“TCP Socket支持定长的返回字节流”功能。 一、 源码本地编译运行 1、在jmeter官网下载源码&#xff1a;jmeter各版本源码地址 2、在idea中用gradle导入jmeter源码&#xff0c;idea中要配置jdk&#xff0c;gradle&#xff0c;我用的是j…

使用flink sqlserver cdc 同步数据到StarRocks

前沿&#xff1a; flink cdc功能越发强大&#xff0c;支持的数据源也越多&#xff0c;本篇介绍使用flink cdc实现&#xff1a; sqlserver-》&#xff08;using flink cdc&#xff09;-〉flink -》&#xff08;using flink starrocks connector&#xff09;-〉starrocks整个流程…

SQL注入类型与技巧

目录 一、注入类型 1.联合查询注入 2.报错注入 3.bool注入(布尔盲注) 4.sleep注入(时间盲注) 二、注入技巧 1.科学计数法绕过正则 2.过滤information 3.无列名注入 一、注入类型 1.联合查询注入 MySQL联合查询注入利用union(联合查询)可以同时执行多条SQL语句的特点…

error: ‘std::_hypot‘ has not been declared using std::hypot;

Cmake 使用qt的编译器 编译opencv时 执行mingw32-make时出现了错误 本质原因就是 _hypot 没有声明。所以找到对应的文件声明一下 就行了。 E:\*****\Qt5.14.1\Tools\mingw730_64\lib\gcc\x86_64-w64-mingw32\7.3.0\include\c 下面的math.h 文件。 可以看到这个文件有一个…

8月31日-9月1日 第六章 案例:MySQL主从复制与读写分离(面试重点,必记)

本章结构 案例概述 案例前置知识点 详细图示 1、什么是读写分离&#xff1f; 读写分离&#xff0c;基本的原理是让主数据库处理事务性增、改、删操作&#xff08;INSERT、UPDATE、DELETE&#xff09;&#xff0c;而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导…

python实现MQTT协议(发布者,订阅者,topic)

python实现MQTT协议 一、简介 1.1 概述 本文章针对物联网MQTT协议完成python实现 1.2 环境 Apache-apollo创建brokerPython实现发布者和订阅者 1.3 内容 MQTT协议架构说明 &#xff1a; 利用仿真服务体会 MQTT协议 针对MQTT协议进行测试 任务1&#xff1a;MQTT协议应…