【設計模式】模板模式,學會它咱也寫出優雅健壯的程式碼!

語言: CN / TW / HK

強行代入

模板方法的定義:

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.

定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟。

它是一種行為型模式。

這寫的是啥?不急,我們先來假設一個場景,通過程式碼來看這說的是啥。

假設:

動物 Animal 這個抽象類,有新陳代謝 metabolism 這個方法,這個方法經過eat()、drink()和sleep()三個動作後執行完成。

小馬 Pony 、小牛 Calf 繼承 Animal

該假設場景的類圖:

程式碼實現

```java /* * 模板方法設計模式 * 正規化重寫的方法,系統幫我們自動呼叫的 * 都可以稱之為模板方法 / public class Main { public static void main(String[] args) { Animal pony = new Pony(); pony.metabolism(); } }

abstract class Animal { /* * 新陳代謝 * 假設都需要經過吃、喝、睡 / public void metabolism() { eat(); drink(); sleep(); }

abstract void eat();

abstract void drink();

abstract void sleep();

}

class Pony extends Animal { @Override void eat() { System.out.println("Pony eating"); }

@Override
void drink() {
    System.out.println("Pony drinking");
}

@Override
void sleep() {
    System.out.println("Pony sleeping");
}

} `` 如果再來一個小牛Calf,它也是小動物,自然也可以繼承自Animal`,如果main方法裡呼叫小牛的新陳代謝方法呢?直接呼叫就完了,不需要逐一呼叫eat、drink和sleep了(自動在metabolism方法裡呼叫)。

```java class Calf extends Animal { @Override void eat() { System.out.println("Calf eating"); }

@Override
void drink() {
    System.out.println("Calf drinking");
}

@Override
void sleep() {
    System.out.println("Calf sleeping");
}

} ```

呼叫:

java public static void main(String[] args) { Animal pony = new Pony(); pony.metabolism(); System.out.println("------------------------"); Animal calf = new Calf(); calf.metabolism(); }

執行結果:

``` Pony eating Pony drinking Pony sleeping


Calf eating Calf drinking Calf sleeping

Process finished with exit code 0 ``` OK,程式寫完了,而且我用到了模板方法設計模式

現在回過頭來看一下,模板方法的定義:

定義一個操作中的演算法骨架,而將演算法的一些步驟延遲到子類中,使得子類可以不改變該演算法結構的情況下重定義該演算法的某些特定步驟。

小動物類Animal的新陳代謝方法metabolism就相當於一個演算法骨架,我們的子類並沒有改變演算法(沒有重寫),只是對演算法內部呼叫的方法繼承下來,每個子類對這些特定步驟有自己的實現而已。

客戶端呼叫的時候只需要呼叫骨架即可,具體的內部方法(eat、drink、sleep),系統會幫我們自動呼叫。

這種模式就是模板方法設計模式

是的,我們一直在用它!

模板方法類圖

這個時候,我們應該已經很熟悉模板方法了,類圖很簡單:

模板方法在Spring中的應用

1. JDBCTemplate

JDBCTemplate是Spring對JDBC的封裝,開發人員自己寫SQL,需要注入dataSource。

2. NamedParameterJdbcTemplate

也是基於JDBC的封裝,不過在引數的書寫上不使用 ? 佔位符,而是使用 :引數名 的形式。

2. RestTemplate

RestTemplate是Spring提供的用於訪問Rest服務的客戶端,RestTemplate提供了多種便捷訪問遠端Http服務的方法,能夠大大提高客戶端的編寫效率。

3. AmqpTemplate

AmqpTemplate介面定義了傳送和接收訊息的所有基本操作。

4. AbstractBeanFactory

下面提供一個簡化版的 AbstractBeanFactory 抽象類,該類實現了獲取例項的 getBean 方法,在方法 getBean 的實現過程中可以看到,主要是對單例 Bean 物件的獲取以及在獲取不到時需要拿到 Bean 的定義做相應 Bean 例項化操作。

getBean 並沒有自身的去實現這些方法,而是隻定義了呼叫過程以及提供了抽象方法,由實現此抽象類的其他類做相應實現。

```java /* * 抽象類定義模板方法 * 繼承了 DefaultSingletonRegistry ,也就具備了使用單例註冊類方法 * * @author 行百里者 * @date 2022-07-17 22:33 / public abstract class AbstractBeanFactory extends DefaultSingletonRegistry implements BeanFactory {

/**
 * 獲取例項
 * 主要是對單例 Bean 物件的獲取以及在獲取不到時需要拿到 Bean 的定義做相應 Bean 例項化操作
 * @param name 例項名稱
 * @return 實體類
 * @throws BeansException 丟擲 Bean 異常資訊
 */
@Override
public Object getBean(String name) throws BeansException {
    //獲取單例類
    Object bean = getSingleton(name);
    if (bean != null) {
        return bean;
    }
    //如果實體類為空,則建立例項(具備了建立例項的能力)
    BeanDefinition beanDefinition = getBeanDefinition(name);
    return createBean(name, beanDefinition);
}

/**
 * 獲取 Bean 定義
 * 由實現此抽象類的其他類做相應的實現
 * @param beanName Bean名稱
 * @return Bean 的定義資訊
 * @throws BeansException 丟擲Bean異常
 */
protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

/**
 * 抽象方法:建立 Bean 例項
 * 由實現此抽象類的其他類做相應的實現
 * @param beanName Bean 名稱
 * @param beanDefinition Bean 的定義資訊
 * @return Bean 實體類
 * @throws BeansException 丟擲Bean異常
 */
protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

} ```

總結模板方法的特點

通過上面的例子,我們可以看到,這種模式的優點: - 它封裝了不變部分,擴充套件可變部分。它把認為是不變部分的演算法封裝到父類中實現,而把可變部分演算法由子類繼承實現,便於子類繼續擴充套件。 - 它在父類中提取了公共的部分程式碼,便於程式碼複用。 - 部分方法是由子類實現的,因此子類可以通過擴充套件方式增加相應的功能,符合開閉原則。

當然我們也能看到,對每個不同的實現都需要定義一個子類,這會導致類的個數增加,系統更加龐大,設計也更加抽象。

以上。

點個贊再走吧~

我正在參與掘金技術社群創作者簽約計劃招募活動,點選連結報名投稿