一、前言
常見的設計模式有23種,我們不得不提到模板方法設計模式,這是一種在軟件開發中廣泛使用的行為型設計模式之一。 它為我們提供了一種優雅的方式來定義算法的結構,并將算法的具體實現延遲到子類中!
在本篇博客中,我們將深入探討模板方法設計模式在Spring Boot中的應用。我們將從概念入手,逐步展開,探究模板方法設計模式原理、優缺點、開源框架應用場景以及如何在企業級靈活應用。
如果您正在尋找一種能夠提升代碼重用性、可維護性和可擴展性的方法,這篇博客一定要收藏。
二、什么是模板方法
全稱是模板方法設計模式,英文是 Template Method Design Pattern。 在 GoF 的《設計模式》一書中,它是這么定義的:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certAIn steps of an algorithm without changing the algorithm’s structure.
翻譯成中文就是:「模板方法模式在一個方法中定義一個算法骨架,并將某些步驟推遲到子類中實現。模板方法模式可以讓子類在不改變算法整體結構的情況下,重新定義算法中的某些步驟。」
這里的“算法”,我們可以理解為廣義上的“業務邏輯”,并不特指數據結構和算法中的“算法”。這里的算法骨架就是“模板”,包含算法骨架的方法就是“模板方法”,這也是模板方法模式名字的由來。
「白話講就是:創建一個抽象類并在里面定義一些方法,有的抽象類本身已經實現,實現方法的復用,有的需要子類去實現提高擴展性!」
三、模板方法的原理
模板方法的原理可以簡單概括如下:
- 「定義算法骨架」:在抽象基類中定義一個模板方法,該方法包含了算法的整體流程,通常由一系列步驟組成。這些步驟可以是抽象方法、具體方法或空方法(鉤子方法)。
- 「子類定制實現」:子類繼承基類,并實現其中的抽象方法,以提供算法的具體實現。子類可以根據需要定制算法的某些步驟,而不必修改整個算法的結構。
- 「模板方法的調用」:在客戶端代碼中,通過調用抽象基類的模板方法來啟動算法。模板方法按照定義的流程調用了各個步驟,以及可能的具體方法或鉤子方法。
總之,通過這種方式,模板方法設計模式實現了方法的復用,可以更好去擴展,同時將算法的整體結構清晰地展現在一個方法中,使得代碼易于理解和維護。
四、優缺點
1、優點
- 「代碼復用」: 模板方法模式鼓勵代碼重用,將通用的算法框架放在抽象類中,可以在多個子類中共享這些通用部分的代碼,減少了重復編寫代碼的工作。
- 「擴展性」: 子類可以通過實現抽象方法或覆蓋鉤子方法來擴展或定制算法的具體步驟,隨時可以擴展,不影響之前代碼。
- 「結構清晰」: 模板方法模式能夠將算法的整體結構清晰地體現出來,使得代碼更易于理解和維護。
- 「符合開閉原則」: 模板方法模式支持開閉原則,因為算法框架在抽象類中定義,具體步驟可以在子類中擴展,而不需要修改抽象類的代碼。
2、缺點
- 「限制靈活性」: 由于模板方法模式固定了算法的整體框架,有時可能會限制一些特定情況下的靈活性。如果需要更細粒度的控制,可能需要通過擴展抽象類來解決。
- 「增加復雜性」: 盡管模板方法模式可以使代碼結構更清晰,但也引入了抽象類和具體子類之間的層次關系,可能會增加代碼的復雜性。
- 「難以理解」: 對新手不友好,可能需要一些時間來理解算法框架和各個具體步驟之間的關系。
五、開源框架應用場景
- JAVA中的java.io.InputStream/OutputStream: Java的輸入輸出流類中使用了模板方法模式。這些類提供了一系列的抽象方法,子類必須實現這些方法來完成底層的讀寫操作。然而,這些類也提供了一些具體的方法,如read和write,這些方法實際上調用了一系列的抽象方法,構成了一個完整的讀寫算法框架。
- Servlet中的HttpServlet: Java Servlet規范中的HttpServlet類也使用了模板方法模式。HttpServlet類提供了service方法來處理HTTP請求,而具體的處理邏輯則通過覆蓋doGet、doPost等方法來實現。
- JUnit測試框架中的TestCase: JUnit測試框架中的TestCase類使用了模板方法模式來定義測試用例的執行流程。用戶可以通過覆蓋setUp和tearDown等方法來定制測試環境的設置和清理。
- Java Swing中的JApplet: Java Swing中的JApplet類也是一個使用模板方法模式的例子。它定義了init、start、stop等方法來控制Applet的生命周期。
有很多博客都會說Spring框架中的JdbcTemplate也是模版方法模式的實踐,看了王爭老師的課才知道,它并非基于模板模式來實現的,而是基于回調來實現的,確切地說應該是同步回調。
可以看一下JdbcTemplate源碼:
@Override
public void execute(final String sql) throws DataAccessException {
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL statement [" + sql + "]");
}
/**
* Callback to execute the statement.
*/
class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider {
@Override
@Nullable
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute(sql);
return null;
}
@Override
public String getSql() {
return sql;
}
}
execute(new ExecuteStatementCallback(), true);
}
六、項目實戰
看了開源框架使用這么多,自己也模擬一個簡單的案例,來體會一下模版方法的魅力!
我們來寫模擬人的一生,經歷五個階段:出生、上學、工作、退休、死亡
其中出生和死亡都是不變的流程,我們進行復用! 上學、工作、退休每個人的人生都是不同的,等著他們自己去實現,這樣來一個新的人都可以創建一個子類去實現,完成了擴展性!
下面我們開始吧!
1、創建父類構造類
/**
* 人的一生模版
* @author wangzhenjun
* @date 2023/8/16 15:07
*/
public abstract class PersonTemplate {
/**
* 人的一生經歷的階段
* @param name
*/
public final void lifeCycle(String name) {
birth(name);
education(name);
work(name);
retirement(name);
death(name);
}
/**
* 教育
* @param name
*/
protected abstract void education(String name);
/**
* 工作
* @param name
*/
protected abstract void work(String name);
/**
* 退休
* @param name
*/
protected abstract void retirement(String name);
/**
* 出生
* @param name
*/
protected void birth(String name) {
System.out.println(name + "哇哇落地了!");
}
/**
* 死亡
* @param name
*/
protected void death(String name) {
System.out.println(name + "退出歷史的舞臺了!");
}
}
「這里父類的方法,如果不想子類去實現,就可以加上final修飾,這個看自己需要,或者不需要每一個都讓子類去實現,可以定義空的方法,有需要的子庫去實現!」
如果是我們的業務復雜這里就自己去拓展方法的參數,來進行后續的操作!
2、創建子類
/**
* @author wangzhenjun
* @date 2023/8/16 16:30
*/
@Component
public class LiHuaPerson extends PersonTemplate{
@Override
protected void education(String name) {
System.out.println(name + "博士畢業了!");
}
@Override
protected void work(String name) {
System.out.println(name + "當上了上市公司CEO!");
}
@Override
protected void retirement(String name) {
System.out.println(name + "留在公司當顧問,不需要上班,工資照發!");
}
}
/**
* @author wangzhenjun
* @date 2023/8/16 16:30
*/
@Component
public class TomPerson extends PersonTemplate{
@Override
protected void education(String name) {
System.out.println(name + "大學畢業了!");
}
@Override
protected void work(String name) {
System.out.println(name + "當上了公務員!");
}
@Override
protected void retirement(String name) {
System.out.println(name + "正常退休,過上遛狗養花的快樂生活!");
}
}
這里就粘貼兩個子類,樣子都是一樣的!
3、創建測試類
@SpringBootTest
class DemoNewApplicationTests {
@Autowired
private TomPerson tomPerson;
@Autowired
private LiHuaPerson liHuaPerson;
@Autowired
private PeterPerson peterPerson;
@Test
void contextLoads() {
tomPerson.lifeCycle("Tom");
liHuaPerson.lifeCycle("LiHua");
peterPerson.lifeCycle("Peter");
}
}
這里就是直接注入了三個子類實現,然后調用,正常業務一般是按需來調用流程,這時可以使用策略模式去改造一下調用端,這個就是按需來進行拓展!
然后結合一下這樣模版方法+策略模式基本上比較完整了!
4、結果
父類的實現方法也執行了,子類的實現方法也執行了!
七、總結
在Spring Boot項目中,整合模板方法設計模式能夠幫助提高代碼的重用性和可維護性,同時在保持一致性的基礎上,為不同場景提供了靈活性。通過深入理解模板方法模式的原理、優缺點以及應用場景,我們可以更好地設計和實現具有高內聚、低耦合的代碼。在實際開發中,合理地運用模板方法模式可以有效地提升代碼質量和開發效率。