模板模式的业务场景在平时开发中并不常见,这个设计模式的核心在于定义逻辑行为的执行顺序,他可以控制整套逻辑的执行顺序和统一的输入输出,而对于实现方只需要关心自己的业务逻辑即可。
但在日常生活中我们经常接触到模板,比如写周报的时候,会有一个周报模板,我们只需要按照模板填写工作内容即可。例如以下是小黄的周报模板,可以把他看作四大块,标题、本周工作、下周工作、总结。每个人填写的内容是不一样的,但是模板制约了填写的内容。
在示例程序中我们实现从不同的网站上爬虫的场景,在此场景中,我们按照顺序实现三个模块:模拟登录,爬取信息,生成海报。
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扮演
模板方法模式通常适用于以下场景。
模版模式的优点
模版模式的缺点