JAVA【设计模式】模板模式
一、定义
模板模式:指定了一系列的算法骨架(方法实现骨架),将算法的一些步骤延迟到子类中,使得子类在不改变算法结构的情况下,重新定义该算法下面的特定步骤实现。它是一种行为模式
二、示例:
模拟场景:
1、模拟爬⾍各类电商商品,⽣成营销推⼴海报场景,⽽整个的爬取过程分为;模拟登录、爬取信息、⽣成海报,这三个步骤。另外每个电商网站的模拟登录、爬取信息、⽣成海报都不一样。
模板模式设计
抽象类,定义了模拟登录、爬取信息、⽣成海报抽象方法,由各大门户不同实现
package com.qf.design.behavior.template.design;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
public abstract class NetMall {
protected Logger logger = LoggerFactory.getLogger(NetMall.class);
//用户id
private String uid;
//用户密码
private String uPwd;
public NetMall(String uid, String uPwd) {
this.uid = uid;
this.uPwd = uPwd;
}
/**
* 生成商品推广海报
*/
public String generateGoodsPoster(String skuUrl){
//1.验证登录
if (!login(uid,uPwd)) return null;
//2.爬虫获取网页信息
Map<String, String> reptile = reptile(skuUrl);
//3.生成商品海报信息
return createBase64(reptile);
}
//模拟登录
public abstract Boolean login(String uid, String uPwd);
//爬虫获取网页信息
public abstract Map<String,String> reptile(String skuUrl);
//生成商品海报信息
public abstract String createBase64(Map<String, String> goodsInfo);
}
爬取数据的公共方法
package com.qf.design.behavior.template.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;
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, "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;
}
}
当当
package com.qf.design.behavior.template.design.group;
import com.alibaba.fastjson.JSON;
import com.qf.design.behavior.template.design.HttpClient;
import com.qf.design.behavior.template.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;
public class DangDangNetMall extends NetMall {
public DangDangNetMall(String uid, String uPwd) {
super(uid, uPwd);
}
@Override
public Boolean login(String uid, String uPwd) {
logger.info("模拟当当⽤户登录 uId:{} uPwd:{}", uid, uPwd);
return true;
}
@Override
public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
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", "4548.00");
logger.info("模拟当当商品爬⾍解析:{} | {} 元 {}", map.get("name"),
map.get("price"), skuUrl);
return map;
}
@Override
public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟⽣成当当商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
JD网
package com.qf.design.behavior.template.design.group;
import com.alibaba.fastjson.JSON;
import com.qf.design.behavior.template.design.HttpClient;
import com.qf.design.behavior.template.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;
public class JDNetMall extends NetMall {
public JDNetMall(String uId, String uPwd){
super(uId, uPwd);
}
@Override
public Boolean login(String uid, String uPwd) {
logger.info("模拟京东⽤户登录 uId:{} uPwd:{}", uid, uPwd);
return true;
}
@Override
public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
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");
logger.info("模拟京东商品爬⾍解析:{} | {} 元 {}", map.get("name"),
map.get("price"), skuUrl);
return map;
}
@Override
public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟⽣成京东商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
taobao网
package com.qf.design.behavior.template.design.group;
import com.alibaba.fastjson.JSON;
import com.qf.design.behavior.template.design.HttpClient;
import com.qf.design.behavior.template.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;
public class TaoBaoNetMall extends NetMall {
public TaoBaoNetMall(String uid, String uPwd) {
super(uid, uPwd);
}
@Override
public Boolean login(String uid, String uPwd) {
logger.info("模拟淘宝⽤户登录 uId:{} uPwd:{}", uid, uPwd);
return true;
}
@Override
public Map<String, String> reptile(String skuUrl) {
String str = HttpClient.doGet(skuUrl);
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", "4799.00");
logger.info("模拟淘宝商品爬⾍解析:{} | {} 元 {}", map.get("name"),
map.get("price"), skuUrl);
return map;
}
@Override
public String createBase64(Map<String, String> goodsInfo) {
BASE64Encoder encoder = new BASE64Encoder();
logger.info("模拟⽣成淘宝商品base64海报");
return encoder.encode(JSON.toJSONString(goodsInfo).getBytes());
}
}
测试:ApiTest
package com.qf.design.behavior.template.design;
import com.qf.design.behavior.template.design.group.DangDangNetMall;
import com.qf.design.behavior.template.design.group.JDNetMall;
import com.qf.design.behavior.template.design.group.TaoBaoNetMall;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiTest {
private Logger logger= LoggerFactory.getLogger(ApiTest.class);
@Test
public void jd_Test(){
NetMall jdNetMall = new JDNetMall("1002001", "1111");
String result = jdNetMall.generateGoodsPoster("https://item.jd.com/100008348542.html");
logger.info("测试结果:{}",result);
}
@Test
public void taobao_Test(){
NetMall jdNetMall = new TaoBaoNetMall("1002001", "1111");
String result = jdNetMall.generateGoodsPoster("https://uland.taobao.com/sem/tbsearch?refpid=mm_26632258_3504122_32538762&keyword=%E6%B7%98%E5%AE%9D&clk1=d50f2fafb28e9f66dc22575a6d3eda87&upsId=d50f2fafb28e9f66dc22575a6d3eda87");
logger.info("测试结果:{}",result);
}
@Test
public void dd_Test(){
NetMall jdNetMall = new DangDangNetMall("1002001", "1111");
String result = jdNetMall.generateGoodsPoster("https://product.dangdang.com/25071550.html");
logger.info("测试结果:{}",result);
}
}
UML关系图
总结:
通过上⾯的实现可以看到 模版模式 在定义统⼀结构也就是执⾏标准上⾮常⽅便,也就很好的控制了后续的实现者不⽤关⼼调⽤逻辑,按照统⼀⽅式执⾏。那么类的继承者只需要关⼼具体的业务逻辑实现即可。
另外模版模式也是为了解决⼦类通⽤⽅法,放到⽗类中设计的优化。让每⼀个⼦类只做⼦类需要完成的内容,⽽不需要关⼼其他逻辑。这样提取公⽤代码,⾏为由⽗类管理,扩展可变部分,也就⾮常有利于开发拓展和迭代。