設計模式之模板方法模式-場景、例子、深入
前言
模板方法的設計模式的使用場景
設計一個系統時知道了算法所需的關鍵步驟,而且確定了這些步驟的執行順序,但某些步驟的具體實現還未知,或者說某些步驟的實現與具體的環境相關。算法的整體步驟很固定,但其中個別部分易變時,這時候可以使用模板方法模式,將容易變的部分抽象出來,供子類實現。
具體場景例子
比如現在我們可以確定獲取指標A,指標B,指標C,指標D,都需要經過,連接初始化,連接,執行,轉換,關閉,五個步驟,並且對每個指標來說除極個別情況來說,初始化,連接,關閉三個步驟內部細節都是一樣的。
具體例子的類圖
1.聲明抽象模板類AbstaractSearchTemplate
package cn.unicom.panabitpendant.TemplateMethod;
import cn.unicom.panabitpendant.pojo.DelayPo;
import com.google.common.collect.Lists;
import io.github.whyareyousoseriously.czcommonutils.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author chenzhen
* Created by chenzhen on 2020/6/23.
*/
abstract class AbstractSearchTemplate<T> {
public static Logger log = LoggerFactory.getLogger(AbstractSearchTemplate.class);
public String user;
public String password;
public String ip;
public String port;
public String dbDriver;
public String table;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
/**
* 數據初始化
*/
public void init(String user, String password, String ip, String port, String dbDriver) {
this.user = user;
this.password = password;
this.ip = ip;
this.port = port;
this.dbDriver = dbDriver;
}
/**
* 連接
*/
public void connection() {
long now = System.currentTimeMillis();
SimpleDateFormat dbNameFormat = new SimpleDateFormat("yyyyMMddHH");
String dbName = "npmflow" + dbNameFormat.format(new Date(now));
SimpleDateFormat tableNameFormat = new SimpleDateFormat("yyyyMMddHHmm");
//List<String> tables = this.generateHoursTable(new Date(now - 60 * 2 * 1000));
table = tableNameFormat.format(new Date(now - 60 * 3 * 1000));
// JDBC 驅動名 及數據庫 URL
final String JDBC_DRIVER = dbDriver;
final String DB_URL = "jdbc:mysql://" + ip + ":" + port + "/" + dbName;
// 數據庫的用戶名與密碼,需要根據自己的設置
final String USER = user;
final String PASS = password;
try {
// 註冊 JDBC 驅動
// 把Driver類裝載進jvm
Class.forName(JDBC_DRIVER);
// 打開鏈接
log.info("連接數據庫..." + DB_URL);
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// 執行查詢
stmt = conn.createStatement();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
/**
* 執行,轉換
*/
public abstract T transform() throws SQLException;
/**
* 關閉
*/
public void close(){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 關閉資源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
log.info("連接結束");
}
public T exec(){
T transform = null;
try {
this.connection();
transform = this.transform();
this.close();
} catch (SQLException e) {
e.printStackTrace();
}
return transform;
}
}
2. 具體子類
2.1 具體子類DnsTopDelaySearch
package cn.unicom.panabitpendant.TemplateMethod;
import cn.unicom.panabitpendant.config.IpPartition;
import cn.unicom.panabitpendant.pojo.DnsDelayPo;
import cn.unicom.panabitpendant.pojo.TopDelayPo;
import com.google.common.collect.Lists;
import io.github.whyareyousoseriously.czcommonutils.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author chenzhen
* Created by chenzhen on 2020/6/23.
*/
@Component
public class DnsTopDelaySearch extends AbstractSearchTemplate<List<DnsDelayPo>> {
@Override
public List<DnsDelayPo> transform() throws SQLException {
List<DnsDelayPo> topDelayPos = Lists.newArrayList();
String sql = "SELECT\n" +
"t.s1 AS sIp, " +
"t.sport AS sPort, " +
"t.d1 AS dIp, " +
"t.dport AS dPort, " +
"t.host AS domainName, " +
"t.appdelay AS appDelay " +
"FROM " + "`" + super.table + "`" + " AS t " +
"WHERE " +
"t.appid = 6 " + " AND " +
"t.pro = 17 " +
"ORDER BY " +
"t.appdelay DESC LIMIT 50" +
";";
super.rs = stmt.executeQuery(sql);
while (rs.next()) {
// 通過字段檢索
double appDelay = super.rs.getDouble("appDelay");
String sIp = IpUtil.longToIP(super.rs.getLong("sIp"));
String sPort = "" + super.rs.getInt("sPort");
String dIp = IpUtil.longToIP(super.rs.getLong("dIp"));
String dPort = "" + super.rs.getInt("dPort");
String domainName = super.rs.getString("domainName");
topDelayPos.add(new DnsDelayPo(sIp, sPort, dIp, dPort, domainName,0.0, 0.0, appDelay));
}
return topDelayPos;
}
}
2.2 具體子類SipTopDelaySearch
package cn.unicom.panabitpendant.TemplateMethod;
import cn.unicom.panabitpendant.config.IpPartition;
import cn.unicom.panabitpendant.pojo.DelayPo;
import com.google.common.collect.Lists;
import io.github.whyareyousoseriously.czcommonutils.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* @author chenzhen
* Created by chenzhen on 2020/6/24.
*/
@Component
public class SipDelaySearch extends AbstractSearchTemplate<List<DelayPo>> {
@Autowired
private IpPartition ipPartition;
@Override
public List<DelayPo> transform() throws SQLException {
List<Map<String, String>> sipPartitionMap = ipPartition.getSipPartitionMap();
List<DelayPo> delayPos = Lists.newArrayList();
for (Map<String, String> stringStringMap : sipPartitionMap) {
String scope = stringStringMap.get("scope");
String[] split = scope.split(",");
for (String s : split) {
String[] se = s.split("-");
long startIp = IpUtil.ipToLong(se[0]);
long endIp = IpUtil.ipToLong(se[1]);
String province = stringStringMap.get("province");
String area = stringStringMap.get("area");
String sql = "SELECT\n" +
"Avg(t.clntdelay) AS avg_clntdelay, " +
"Avg(t.svrdelay) AS avg_svrdelay, " +
"Avg(t.appdelay) AS avg_appdelay, " +
"MAX(t.clntdelay) AS max_clntdelay, " +
"MAX(t.svrdelay) AS max_svrdelay, " +
"MAX(t.appdelay) AS max_appdelay, " +
"MIN(t.clntdelay) AS min_clntdelay, " +
"MIN(t.svrdelay) AS min_svrdelay, " +
"MIN(t.appdelay) AS min_appdelay, " +
"AVG(t.retmixpkts1/t.pkts1) AS avg_up_rr, " +
"MIN(t.retmixpkts1/t.pkts1) AS min_up_rr, " +
"MAX(t.retmixpkts1/t.pkts1) AS max_up_rr, " +
"AVG(t.retmixpkts2/t.pkts2) AS avg_down_rr, " +
"MIN(t.retmixpkts2/t.pkts2) AS min_down_rr, " +
"MAX(t.retmixpkts2/t.pkts2) AS max_down_rr " +
"FROM " + "`" + table + "`" + " AS t " +
"WHERE " +
"t.s1 >= " + startIp + " AND " +
"t.s1 <= " + endIp + " AND " +
"t.pro = 17" + " AND " +
//"t.appid = 61" + " AND " +
"t.appdelay > 0" +
";";
rs = stmt.executeQuery(sql);
while (rs.next()) {
// 通過字段檢索
double clientDelay = rs.getDouble("avg_clntdelay");
double serviceDelay = rs.getDouble("avg_svrdelay");
double appDelay = rs.getDouble("avg_appdelay");
double maxDelay = rs.getDouble("max_clntdelay");
double maxServiceDelay = rs.getDouble("max_svrdelay");
double maxAppDelay = rs.getDouble("max_appdelay");
double minDelay = rs.getDouble("min_clntdelay");
double minServiceDelay = rs.getDouble("min_svrdelay");
double minAppDelay = rs.getDouble("min_appdelay");
double avgUpRr = rs.getDouble("avg_up_rr");
double minUpRr = rs.getDouble("min_up_rr");
double maxUpRr = rs.getDouble("max_up_rr");
double avgDownRr = rs.getDouble("avg_down_rr");
double minDownRr = rs.getDouble("avg_down_rr");
double maxDownRr = rs.getDouble("avg_down_rr");
delayPos.add(new DelayPo(province, area, clientDelay, serviceDelay, appDelay, maxDelay, maxServiceDelay, maxAppDelay,
minDelay, minServiceDelay, minAppDelay, avgUpRr, minUpRr, maxUpRr, avgDownRr, minDownRr, maxDownRr));
}
}
}
return delayPos;
}
}
3. 具體子類的使用
3.1 具體子類DnsTopDelaySearch的使用
public List<DnsDelayPo> dnsTopDelay() {
dnsTopDelaySearch.init(user, password, ip, port, dbDriver);
return dnsTopDelaySearch.exec();
}
3.2 具體子類SipTopDelaySearch的使用
public List<TopDelayPo> getSipTopDelay() {
sipTopDelaySearch.init(user, password, ip, port, dbDriver);
return sipTopDelaySearch.exec();
}
4. 鉤子
鉤子方法的返回結果決定了模板方法後面部分的執行步驟,也就是程序接下來的走向,
這樣一來,程序就擁有了變化的可能。
未完待續