• 设计模式之模版模式


    模板模式

    概述

    模板模式的业务场景在平时开发中并不常见,这个设计模式的核心在于定义逻辑行为的执行顺序,他可以控制整套逻辑的执行顺序和统一的输入输出,而对于实现方只需要关心自己的业务逻辑即可。

    但在日常生活中我们经常接触到模板,比如写周报的时候,会有一个周报模板,我们只需要按照模板填写工作内容即可。例如以下是小黄的周报模板,可以把他看作四大块,标题、本周工作、下周工作、总结。每个人填写的内容是不一样的,但是模板制约了填写的内容。

    在这里插入图片描述

    示例程序

    在示例程序中我们实现从不同的网站上爬虫的场景,在此场景中,我们按照顺序实现三个模块:模拟登录,爬取信息,生成海报。

    在这里插入图片描述

    代码实现

    NetMail抽象类

    NetMail可以看作是我们的模版,它规定了流程的具体实现步骤,而不规定每个步骤之间的具体实现,具体实现交给它的子类完成

    @Slf4j
    public abstract class NetMail {
        protected String username;
        protected String password;
    
        public NetMail(String username, String password) {
            this.username = username;
            this.password = password;
        }
    
        /**
         * 生成商品推广海报
         *
         * @param skuUrl 商品地址(京东、淘宝、当当)
         * @return 海报图片base64位信息
         */
        public String generateGoodsPoster(String skuUrl) throws JsonProcessingException {
            if (!login(username, password)) return null;             // 1. 验证登录
            Map<String, String> reptile = reptile(skuUrl);  // 2. 爬虫商品
            return createBase64(reptile);                   // 3. 生成海报
        }
    
        //验证登录
        protected abstract boolean login(String username,String password);
        //爬虫商品
        protected abstract Map<String,String> reptile(String skuUrl);
        //生成海报
        protected abstract String createBase64(Map<String,String> reptile) throws JsonProcessingException;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    具体的实现类

    具体的实现类实现了验证登录、爬虫商品、生成海报三个方法,不同的实现类实现方法的方式不同

    //京东
    @Slf4j
    public class JDNetMail extends NetMail {
        public JDNetMail(String username, String password) {
            super(username, password);
        }
    
        @Override
        protected boolean login(String username, String password) {
            log.info("模拟京东登录:username:{},password:{}",username,password);
            return true;
        }
    
        @Override
        protected Map<String, String> reptile(String skuUrl) {
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> entity = restTemplate.getForEntity(skuUrl, String.class);
            String str = entity.getBody();
            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");
            log.info("模拟京东商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);
            return map;
        }
    
        @Override
        protected String createBase64(Map<String, String> reptile) throws JsonProcessingException {
            BASE64Encoder encoder = new BASE64Encoder();
            log.info("模拟京东商品生成海报");
            return encoder.encode(new ObjectMapper().writeValueAsBytes(reptile));
        }
    }
    
    //淘宝
    @Slf4j
    public class TBNetMail extends NetMail {
        public TBNetMail(String username, String password) {
            super(username, password);
        }
    
        @Override
        protected boolean login(String username, String password) {
            log.info("模拟淘宝登录:username:{},password:{}",username,password);
            return true;
        }
    
        @Override
        protected Map<String, String> reptile(String skuUrl) {
            RestTemplate restTemplate = new RestTemplate();
            ResponseEntity<String> entity = restTemplate.getForEntity(skuUrl, String.class);
            String str = entity.getBody();
            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");
            log.info("模拟淘宝商品爬虫解析:{} | {} 元 {}", map.get("name"), map.get("price"), skuUrl);
            return map;
        }
    
        @Override
        protected String createBase64(Map<String, String> reptile) throws JsonProcessingException {
            BASE64Encoder encoder = new BASE64Encoder();
            log.info("模拟淘宝商品生成海报");
            return encoder.encode(new ObjectMapper().writeValueAsBytes(reptile));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    测试

    @SpringBootTest
    class Practice2400ApplicationTests {
    
        @Test
        void contextLoads() throws JsonProcessingException {
            NetMail netMail = new JDNetMail("yellowstar","123456");
            String poster = netMail.generateGoodsPoster("https://item.jd.com/100026354913.html");
        }
    
    }
    
    //结果
    2022-06-29 21:50:40.581  INFO 10678 --- [           main] c.y.practice2400.design.impl.JDNetMail   : 模拟京东登录:username:yellowstar,password:123456
    2022-06-29 21:50:43.974  INFO 10678 --- [           main] c.y.practice2400.design.impl.JDNetMail   : 模拟京东商品爬虫解析:【创维创维C2】创维(Skyworth)男士电动剃须刀C2刀头可水洗刮胡刀充电式胡须刀男孩子刮胡子刀男生智能剃胡子刀【行情 报价 价格 评测】-京东 | 5999.00 元 https://item.jd.com/100026354913.html
    2022-06-29 21:50:43.975  INFO 10678 --- [           main] c.y.practice2400.design.impl.JDNetMail   : 模拟京东商品生成海报
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    登场角色

    • AbstractClass(抽象类)

      AbstractClass角色不仅负责实现模版方法,还负责声明在模版方法中所使用到的抽象方法,这些抽象方法由AbstractClass角色的子类实现,在示例程序中由NetMail扮演该角色

    • ConcreteClass(具体的实现类)

      ConcreteClass角色负责实现AbstractClass角色定义的抽象方法,这里实现的方法将会在AbstractClass角色的模版方法中调用。在示例程序中由JDNetMail、TBNetMail扮演

    适用场景

    模板方法模式通常适用于以下场景。

    1. 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
    2. 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
    3. 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

    总结

    模版模式的优点

    • 可以使逻辑处理通用化,由于在父类的模版方法中编写了逻辑算法,因此无需在每个字类中在编写算法
    • 父类与子类的一致性,在示例程序中,无论是哪个实现类username、password属性都是保存在父类中的,使用父类保存子类实例的优点是,即是没有用instanceof等指定子类的种类,程序也能正常工作

    模版模式的缺点

    • 因为父类和子类是紧密联系、共同工作的,因此在子类中实现父类的抽象方法,必须要理解这些抽象方法被调用的时机,在看不到父类的源代码的情况下,想要编写子类是非常困难的
  • 相关阅读:
    基于JSP的智能道路交通信息管理系统
    联想Y9000P-2022款踩坑指南 加装固态和内存 win10系统安装
    MySQL常见的数据查询慢甚至导致死锁问题
    rabbitmq简记
    IvorySQL3.0:基于PG16.0最新内核,实现兼容Oracle数据库再升级
    THM-Linux提权-权限升级
    ClearML入门:简化机器学习解决方案的开发和管理
    AQS之ReentrantLock源码解析
    flutter,javascript forEach await无效
    Github忘记了Two-factor Authentication code
  • 原文地址:https://blog.csdn.net/Yellow_Star___/article/details/125530442