設計模式之模板方法模式

在我們實際開發中,如果一個方法極其複雜時,如果我們將所有的邏輯寫在一個方法中,那維護起來就很困難,要替換某些步驟時都要重新寫,這樣代碼的擴展性就很差,當遇到這種情況就要考慮今天的主角——模板方法模式。

一、概念理解

模板方法模式的概念很簡單,在一個方法中定義一個算法的骨架,而將一些步驟延遲到子類中,模板方法使得子類可以在不改變算法結構的情況下,重新定義算法中的某些步驟。

既然概念叫「骨架」,那想當然的就是定義一個抽象類,這是模板方法模式的第一個角色——抽象模板角色,要有延遲子類實現骨架方法,這是模板方法的第二個角色——具體模板角色。

抽象模板角色:定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟,定義並實現了一個模板方法。

具體模板角色:實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。每一個抽象模板角色都可以有任意多個具體模板角色與之對應。

二、案例實現

在我們的業務開發中往往都需要很多對象、很多方法,對象間也大都存在依賴關係,如果我們手動創建、管理對象就是一件極其困難的事。

如果我們使用工廠模式用於創建對象,使用一個容器用於管理對象,那麼再使用起來就變得極其簡單了。

在「這個過程」中創建對象就是一個很複雜的算法,而且創建對象的方式往往也不是單一的,我們要考慮能替換算法,這時候就可以使用模板方法模式。

假設創建對象有兩種方式,一種是基於註解,一種是基於xml,我們就將該方法定義為一個模板方法,基於註解和基於xml讓子類去實現。

我們用refresh()方法代表這個複雜的過程,在這個過程中應該包括:

①開始工作前的預處理;

②創建管理對象的容器(模板方法,基於註解和基於XML交給子類實現);

③模板方法(交給子類,方便擴展);

④其他方法(容器刷新後、國際化、應用監聽、發佈事件,等等等一堆事)。

我們基於模板方法模式我們實現簡單的demo。

抽象模板角色:

我們在抽象模板角色中實現部分邏輯,而創建對象的容器obtainFreshBeanFactory()方法交給子類實現,onRefresh()空方法交給子類實現便於擴展。

/**
 * 抽象模板角色
 * @author tcy
 * @Date 28-09-2022
 */
public abstract class AbstractApplicationContext {

    /**
     * 案例中容器和對象的創建全過程
     */
    public void refresh(){

        this.prepareRefresh();

        this.obtainFreshBeanFactory();

        this.onRefresh();
    }

    protected void prepareRefresh(){
        System.out.println("我用於開始工作前的預處理...");
    }

    protected void obtainFreshBeanFactory(){
        System.out.println("我用於創建默認管理對象的容器...");
    }

    /**
     * 模板方法,子類去實現[springboot實現了他,感興趣具體可以研究一下]
     */
    protected void onRefresh() {
    }


    protected void otherMethod(){
        System.out.println("容器刷新後、國際化、應用監聽、發佈事件,等等等,一堆事");
    }
}

具體模板角色-基於註解創建管理對象的容器

/**
 * 具體模板角色-基於註解
 * @author tcy
 * @Date 28-09-2022
 */
public class ApplicationContextAnnotation extends AbstractApplicationContext {
    @Override
    protected void obtainFreshBeanFactory() {
        System.out.println("這是基於註解的創建對象容器...");
    }
}

具體模板角色-基於xml創建管理對象的容器

/**
 * 具體模板角色-基於xml
 * @author tcy
 * @Date 28-09-2022
 */
public class ApplicationContextXml extends AbstractApplicationContext {
    @Override
    protected void obtainFreshBeanFactory() {
        System.out.println("這是xml的創建對象容器...");
    }
}

客戶端-模擬容器啟動過程:

/**
 * 容器啟動過程
 * @author tcy
 * @Date 28-09-2022
 */
public class Client {
    public static void main(String[] args) {

		//基於xml方式
        ApplicationContextXml applicationContextXml = new ApplicationContextXml();
        applicationContextXml.refresh();

		//基於註解方式
        ApplicationContextAnnotation annotation=new ApplicationContextAnnotation();
        annotation.refresh();

    }
}
我用於開始工作前的預處理...
這是xml的創建對象容器...
容器刷新後、國際化、應用監聽、發佈事件,等等等,一堆事
我用於開始工作前的預處理...
這是基於註解的創建對象容器...
容器刷新後、國際化、應用監聽、發佈事件,等等等,一堆事

對Spring源碼稍微有點了解的同學大概已經知道,我們案例實現的正是簡易版的Spring的Refresh()方法,Refresh()方法是Spring最核心的方法,Spring良好的擴展性正是離不開模板方法模式的運用。下圖為Spring核心Refresh()方法的執行大流程注釋。

image-20220929094055185

在我們案例中的onRefresh()的空方法,實際中Springboot就是實現了這個空方法,onRefresh()方法調用了Tomcat的jar包啟動,這也是Springboot不需要手動注入Tomcat的原因。

相信通過這個案例的理解,大部分同學不僅能很好的理解模板方法模式,想必對Spring的啟動過程也有了一個大概的了解。

三、總結

模板方法應用場景太普遍了,在實際開發中有多個子類共有的方法,並且邏輯相同,可以考慮使用模板方法模式。當面對重要、複雜的算法,也可以把核心算法設計為模板方法模式,相關細節則由各個子類實現。

模板方法優點突出:封裝不變部分,擴展可變部分;提取公共代碼,便於維護;行為由父類控制,子類實現。

模板方法的缺點很明顯,當方法實現過多的時候,每一個不同的實現都需要一個子類來實現,這必然導致類的個數增加,使得系統變得更加龐大。

個人獨立開發的應用框架芒果管理系統後端,支持前後端代碼生成、支持字段註解等實用開發功能已全部開源,感興趣的同學可以點個star鼓勵我一下。這是芒果管理系統前端

設計模式的學習要成體系,推薦你看我往期發佈的設計模式文章。

一、設計模式概述

二、設計模式之工廠方法和抽象工廠

三、設計模式之單例和原型

四、設計模式之建造者模式

五、設計模式之代理模式

六、設計模式之適配器模式

七、設計模式之橋接模式

八、設計模式之組合模式

九、設計模式之裝飾器模式

十、設計模式之外觀模式

十一、外觀模式之享元模式

十二、設計模式之責任鏈模式

十三、設計模式之命令模式

十四、設計模式之解釋器模式

十五、設計模式之迭代器模式

十六、設計模式之中介者模式

十七、設計模式之備忘錄模式

十八、設計模式之觀察者模式

十九、設計模式之狀態模式

二十、設計模式之策略模式