设计模式之模版模式

模板模式

概述

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

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

在这里插入图片描述

示例程序

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

在这里插入图片描述

代码实现

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;
}

具体的实现类

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

//京东
@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));
    }
}

测试

@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   : 模拟京东商品生成海报

登场角色

  • AbstractClass(抽象类)

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

  • ConcreteClass(具体的实现类)

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

适用场景

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

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

总结

模版模式的优点

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

模版模式的缺点

  • 因为父类和子类是紧密联系、共同工作的,因此在子类中实现父类的抽象方法,必须要理解这些抽象方法被调用的时机,在看不到父类的源代码的情况下,想要编写子类是非常困难的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值