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连接对象打开一个连接,强转成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

四、总结:模板模式

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

切换Java版本

Mac安装不同Java版本 在Sentinel限流框架的使用中&#xff0c;Java版的Sentinel提供一个可以起Dashboard的jar包。访问项目接口&#xff0c;按预期应该在Dashboard里有数据。发现多次请求后还是空白。 仔细看Dashboard的日志&#xff0c;疑似是Java版本的问题&#xff0c;搜了下…

无涯教程-机器学习 - 箱形图函数

Box和Whisker图(也简称为boxplots)是另一种有用的技术&#xff0c;可用于检查每个属性的分布情况。以下是此技术的特点- 它本质上是单变量的&#xff0c;总结了每个属性的分布。它为中间值(即中位数)画一条线。它将在25&#xff05;和75&#xff05;周围绘制一个框。它还会绘制…

SAP PP之定义活动/作业类型(Activity Type)

文章目录 前言 一、作业是什么 二、使用步骤 1.单独创建 2.创建组 注意点 前言 创建活动类型具有以下先决条件&#xff1a; 控制范围已创建并分配给公司代码。已创建成本要素类别为43的次要成本要素。 一、作业是什么 SAP活动类型是在成本范围的成本中心中产生的活动的分类。…

css强制显示一行

要强制将文本内容显示在一行中&#xff0c;可以使用CSS的white-space属性和overflow属性来实现。 首先&#xff0c;将white-space属性设置为nowrap&#xff0c;这样文本内容就不会换行。然后&#xff0c;将overflow属性设置为hidden&#xff0c;这样超出一行的内容就会被隐藏起…

BDCC - 闲聊数据仓库的架构

文章目录 典型数据仓库架构图数据仓库ETL vs ELTETLELT区别联系 数据仓库分层&#xff08;1&#xff09;数据仓库ODS层&#xff08;2&#xff09;数据仓库CDM层DWD数据明细层DWS数据汇总层 &#xff08;3&#xff09;数据仓库ADS层 典型数据仓库架构图 按自下而上的顺序&#x…

《Python趣味工具》——其他常见的RPG游戏梳理:

Hello&#xff0c;各位朋友们大家好&#xff01;昨天我们一起制作了自己的第一个RPG游戏——《人生选择模拟器》&#xff0c;是不是还意犹未尽呢&#xff1f;哈哈&#xff0c;今天我们再来尝试做几款比较轻量级的小游戏吧&#xff01; 文章目录 1. 猜单词游戏:2. 姻缘测试:3. …

通过这 5 项 ChatGPT 创新增强您的见解

为什么绝大多数的人还不会使用chatGPT来提高工作效能&#xff1f;根本原因就在还不会循序渐进的发问与chatGPT互动。本文总结了5个独特的chatGPT提示&#xff0c;可以帮助您更好地与Chat GPT进行交流&#xff0c;以获得更清晰的信息、额外的信息和见解。 澄清假设和限制 用5种提…

Vue前端的一些表格组件的思考

当我们需要在前端中展示一些表格内容时&#xff0c;我们往往使用Vue的table来实现 1. 原生态实现 <template><div><table class"no-gap-table"><thead><tr><th class"styled-header" colspan"4">Column1&…

Linux 操作系统实战视频课 - GPIO 基础介绍

文章目录 一、GPIO 概念说明二、视频讲解沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将讲解 GPIO 。 一、GPIO 概念说明 ARM 平台中的 GPIO(通用输入/输出)是用于与外部设备进行数字输入和输出通信的重要硬件接口。ARM 平台的 GPIO 特性可以根据具体的芯…

六、高并发内存池--Central Cache

六、高并发内存池–Central Cache 6.1 Central Cache的工作原理 central cache也是一个哈希桶结构&#xff0c;他的哈希桶的映射关系跟thread cache是一样的。不同的是他的每个哈希桶位置挂是SpanList链表结构&#xff0c;不过每个映射桶下面的span中的大内存块被按映射关系切…

IPV4地址说明

设想一个场景&#xff1a; 你有两台电脑A和B&#xff0c;需要把A的数据传输到B&#xff0c;怎么办&#xff1f; 1 我们可以用U盘进行拷贝&#xff0c;就是把A的数据拷贝到B 2 我们可以用一根网线把AB连接起来 显然&#xff0c;两台电脑用一根网线。那要是n台电脑呢&#xff1f;…

进程管理死死的学

进程管理 文件属性 chattr【扩展】 chattr chattr i 文件名 # 添加权限 a 可追加&#xff0c;不可修改 i 只可查看 A 不修改访问时间 charrt -i 文件名 # 取消权限 -R 递归处理&#xff0c;将指令目录下的所有文件及子目录一并处理&#xff1b;lsattr 查看文件属性 lsattr …

事务的总结

数据库事务 数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行&#xff0c;要么完全不执行。事务管理是一个重要组成部分&#xff0c;RDBMS 面向企业应用程序&#xff0c;以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性描述…

js对中文进行base64编码和解码操作,解决中文乱码问题

我使用github api的接口获取文件内容&#xff0c;然后使用atob进行解码&#xff0c;但是发现&#xff1a;乱码.......糟心啊 所以就有了我封装的方法&#xff1a; export const encode64 (str) > {// 首先&#xff0c;我们使用 encodeURIComponent 来获得百分比编码的UTF…

Python数据分析案例30——中国高票房电影分析(爬虫获取数据及分析可视化全流程)

案例背景 最近总看到《消失的她》票房多少多少&#xff0c;《孤注一掷》票房又破了多少多少..... 于是我就想自己爬虫一下获取中国高票房的电影数据&#xff0c;然后分析一下。 数据来源于淘票票&#xff1a;影片总票房排行榜 (maoyan.com) 爬它就行。 代码实现 首先爬虫获…

Django传递dataframe对象到前端网页

在django前端页面上展示的数据&#xff0c;还是使用django模板自带的语法 方式1 不推荐使用 直接使用 【df.to_html(indexFalse)】 使用to_html他会生成一个最基本的表格没有任何的样式&#xff0c;一点都不好看&#xff0c;如果有需要的话可以自行修改表格的样式&#xff0c;…

【教程】部署apprtc服务中安装google-cloud-cli组件的问题及解决

#0# 前置条件 已经安装完成node&#xff0c;grunt&#xff0c;node 组件和python pip包等。需要安装google-cloud-cli组件。 Ubuntu安装google-cloud-cli组件 apprtc项目运行需要google-cloud-cli前置组件&#xff0c;且运行其中的dev_appserver.py。 根据google官方的关于安…

如何使用CSS实现一个自适应等高布局?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Flexbox 布局⭐ 使用 Grid 布局⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发…

Linux(实操篇三)

Linux实操篇 Linux(实操篇三)1. 常用基本命令1.7 搜索查找类1.7.1 find查找文件或目录1.7.2 locate快速定位文件路径1.7.3 grep过滤查找及"|"管道符 1.8 压缩和解压类1.8.1 gzip/gunzip压缩1.8.2 zip/unzip压缩1.8.3 tar打包 1.9 磁盘查看和分区类1.9.1 du查看文件和…

RT_Thread内核机制学习(六)信号量

要传输较大数据时&#xff0c;使用队列。 传输较小数值时&#xff0c;使用邮箱。 队列、邮箱用来传递数据。 如果只是用来传递资源的个数&#xff0c;可以使用信号量。 A车与B车只需要传递信号量&#xff08;代表资源&#xff09;。 信号量 获取信号量 如果value>0&…