模板方法模式是一種行為型設計模式,它定義一個操作(模板方法)的基本組合與控制流程,將一些步驟(抽象方法)推遲到子類中,在使用時呼叫不同的子類,就可以達到不改變一個操作的基本流程情況下,即可修改其中的某些特定步驟。這種設計方式將特定步驟的具體實現與操作流程分離開來,實現了程式碼的複用和擴充套件,從而提高程式碼質量和可維護性。
模板方法模式包含以下:
模板方法模式的優點:
模板方法模式的缺點:
簡單列一些模板方法模式的應用場景:
Spring
中的 JdbcTemplate、RestTemplate、RabbitTemplate、KafkaTemplate
等。如上,我們用一個簡單的傳送簡訊程式碼來做模板方法模式的範例:
定義一個傳送簡訊模板
/**
* 傳送簡訊模板
*/
public abstract class SmsTemplate {
/**
* 傳送方法
*
* @param mobile 手機號
*/
public void send(String mobile) throws Exception {
System.out.println("檢查使用者一分鐘內是否傳送過簡訊,
mobile:" + mobile);
if (checkUserReceiveInOneMinute(mobile)) {
throw new Exception("請等待1分鐘後重試");
}
String code = genCode();
if (manufacturer(mobile, code)) {
System.out.println("簡訊廠商傳送簡訊成功,
mobile:" + mobile + ",code=" + code);
save2redis(mobile, code);
}
}
/**
* 模板方法,由不同的廠商來實現傳送簡訊到手機上
* @return
*/
abstract boolean manufacturer(String mobile, String code);
/**
* 檢查1分鐘內該手機號是否接收過驗證碼,1分鐘內接收過就不能在傳送驗證碼
* @param mobile
* @return
*/
public boolean checkUserReceiveInOneMinute(String mobile) {
return ...;
}
/**
* 生成6位驗證碼
* @return
*/
public String genCode() {
return "123456";
}
/**
* 將手機號+驗證碼存進redis中,給登入介面做校驗用
* @param mobile
* @param code
*/
public void save2redis(String mobile, String code) {
...
}
}
新增兩個不同廠商實現的子類
/**
* 阿里雲簡訊傳送
*/
public class AliyunSmsSend extends SmsTemplate{
@Override
boolean manufacturer(String mobile, String code) {
System.out.println("讀取阿里雲簡訊設定");
System.out.println("建立阿里雲傳送簡訊使用者端");
System.out.println("阿里雲傳送簡訊成功");
return true;
}
}
/**
* 騰訊雲簡訊傳送
*/
public class TencentSmsSend extends SmsTemplate {
@Override
boolean manufacturer(String mobile, String code) {
System.out.println("讀取騰訊雲簡訊設定");
System.out.println("建立騰訊雲傳送簡訊使用者端");
System.out.println("騰訊雲傳送簡訊成功");
return true;
}
}
在 Java 程式中進行呼叫
public class Main {
public static void main(String[] args) throws Exception {
SmsTemplate smsTemplate1 = new AliyunSmsSend();
smsTemplate1.send("13333333333");
System.out.println("---------------------------");
SmsTemplate smsTemplate2 = new TencentSmsSend();
smsTemplate2.send("13333333333");
}
}
輸出如下:
檢查使用者一分鐘內是否傳送過簡訊,mobile:13333333333
讀取阿里雲簡訊設定
建立阿里雲傳送簡訊使用者端
阿里雲傳送簡訊成功
簡訊廠商傳送簡訊成功,mobile:13333333333,code=123456
---------------------------
檢查使用者一分鐘內是否傳送過簡訊,mobile:13333333333
讀取騰訊雲簡訊設定
建立騰訊雲傳送簡訊使用者端
騰訊雲傳送簡訊成功
簡訊廠商傳送簡訊成功,mobile:13333333333,code=123456
我們來看看模板方法模式的組成:
SmsTemplate
中定義了傳送簡訊的基本流程操作
AliyunSmsSend、TencentSmsSend
繼承抽象類,實現抽象方法 manufacturer(String mobile, String code)
,定義流程中的可變部分。send(mobile)
,在模板方法中完成了基本流程組合與條件控制。在 Spring
中實現模板方法模式,是非常簡單的,我們只需要對上述的 Java
程式碼範例的 AliyunSmsSend
類稍作改造,加上 @Component
註解就行,
/**
* 阿里雲簡訊傳送
*/
@Component
public class AliyunSmsSend extends SmsTemplate{
@Override
boolean manufacturer(String mobile, String code) {
IUserService userService = SpringUtil.getBean(IUserService.class);
System.out.println("讀取阿里雲簡訊設定");
System.out.println("建立阿里雲傳送簡訊使用者端");
System.out.println("阿里雲傳送簡訊成功");
return true;
}
}
如果在 AliyunSmsSend
類中需要注入其他 bean
,通過 cn.hutool.extra.spring.SpringUtil.getBean(...)
方法獲取對應 bean
就行。
在Java8 中,還可以使用函數表示式來替換抽象方法,程式碼如下,
/**
* 傳送簡訊模板
*/
public class SmsTemplateLambda {
/**
* 傳送簡訊
* @param mobile 手機號
* @param biFunction
* @throws Exception
*/
public void send(String mobile,
BiFunction<String, String, Boolean> biFunction) throws Exception {
System.out.println("檢查使用者一分鐘內是否傳送過簡訊,mobile:" + mobile);
if (checkUserReceiveInOneMinute(mobile)) {
throw new Exception("請等待1分鐘後重試");
}
String code = genCode();
if (biFunction.apply(mobile, code)) {
System.out.println("簡訊廠商傳送簡訊成功,mobile:"
+ mobile + ",code=" + code);
save2redis(mobile, code);
}
}
...
}
通過 BiFunction
函數,將不同廠商傳送簡訊到使用者手機的程式碼在 send(mobile)
方法中分離處理。
呼叫方法如下:
public static void main(String[] args) throws Exception {
SmsTemplateLambda smsTemplateLambda = new SmsTemplateLambda();
smsTemplateLambda.send("1333333333", (s, s2) -> {
System.out.println("讀取阿里雲簡訊設定");
System.out.println("建立阿里雲傳送簡訊使用者端");
System.out.println("阿里雲傳送簡訊成功");
return true;
});
smsTemplateLambda.send("1333333333", (s, s2) -> {
System.out.println("讀取騰訊雲簡訊設定");
System.out.println("建立騰訊雲傳送簡訊使用者端");
System.out.println("騰訊雲傳送簡訊成功");
return true;
});
}
可以看到,我們可以只在呼叫 SmsTemplateLambda
類的 send(mobile)
方法時,才實現不同廠商傳送簡訊到手機的具體邏輯。好處就是每增加一個模板方法時,不用增加具體的子類實現,減少類的建立與降低子類的實現成本。
模板方法模式通過定義一個流程基本操作也就是模板方法,將具體的實現步驟推遲到子類中,使得子類可以靈活地實現可變的行為,這是模板方法模式的核心思想與價值所在。
關注公眾號【waynblog】每週分享技術乾貨、開源專案、實戰經驗、高效開發工具等,您的關注將是我的更新動力!