1. 程式人生 > >[設計模式] 模板模式(Template Pattern)

[設計模式] 模板模式(Template Pattern)

原文連結
https://www.cnblogs.com/qq-361807535/p/6854191.html

引入:這幾天在看一本講Spring原始碼的書《SPRING技術內幕》裡面在講載入配置檔案的時候,可以有不同的載入方式,如根據檔案系統目錄載入配置檔案(FileSystemXmlApplicationContext),類路徑載入配置檔案(ClassPathXmlApplicationContext), 以及根據專案上下文目錄(XmlWebApplicationContext)載入配置檔案。這個在載入過程中使用了模板設計模式,所以就來學習模板設計模式。

模板設計模式在書中定義

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

通俗點的理解就是:完成一件事情,有固定的個數步驟,但是每個步驟根據物件的不同,而實現細節不同;就可以在父類中定義一個完成該事情的總方法,按照完成事件需要的步驟去呼叫其每個步驟的實現方法。每個步驟的具體實現,由子類完成。

如下網上找到一個模板模式的類圖:

抽象父類(AbstractClass):實現了模板方法,定義了演算法的骨架。
具體類(ConcreteClass):實現抽象類中的抽象方法,即不同的物件的具體實現細節。

這裡寫圖片描述

##例項說明
來舉個例子:比如我們做菜可以分為三個步驟:

  • 備料
  • 具體做菜
  • 盛菜端給客人享用
    這三部就是演算法的骨架,然而做不同菜需要的料,做的方法,以及如何盛給客人享用都是沒的,這個就是不同的實現細節。

下面我們的程式碼實現如下

先來寫一個抽象的做菜父類:

public abstract class DodishTemplate {

    public void dodish() {
        preparation();
        doing();
        carriedDishes();
    }

    public abstract void preparation();

    public abstract void doing();

    public abstract void carriedDishes();
}

下來做兩個番茄炒蛋(EggsWithTomato)和紅燒肉(Bouilli)實現父類中的抽象方法

public class EggsWithTomato extends DodishTemplate {
    @Override
    public void preparation() {
        System.out.println("洗並切西紅柿,打雞蛋。");
    }

    @Override
    public void doing() {
        System.out.println("雞蛋倒入鍋裡,然後倒入西紅柿一起炒。");
    }

    @Override
    public void carriedDishes() {
        System.out.println("將炒好的西紅寺雞蛋裝入碟子裡,端給客人吃。");

    }
}

public class Bouilli extends DodishTemplate {
    @Override
    public void preparation() {
        System.out.println("切豬肉和土豆。");
    }

    @Override
    public void doing() {
        System.out.println("將切好的豬肉倒入鍋中炒一會然後倒入土豆連炒帶燉。");
    }

    @Override
    public void carriedDishes() {
        System.out.println("將做好的紅燒肉盛進碗裡端給客人吃。");
    }
}

在測試類中我們來做菜

public class App {
    public static void main(String[] args) {
        DodishTemplate eggsWithTomato = new EggsWithTomato();
        eggsWithTomato.dodish();

        System.out.println("------------");
        DodishTemplate bouilli = new Bouilli();
        bouilli.dodish();
    }
}
/* output
洗並切西紅柿,打雞蛋。
雞蛋倒入鍋裡,然後倒入西紅柿一起炒。
將炒好的西紅寺雞蛋裝入碟子裡,端給客人吃。
------------
切豬肉和土豆。
將切好的豬肉倒入鍋中炒一會然後倒入土豆連炒帶燉。
將做好的紅燒肉盛進碗裡端給客人吃。

*/

這樣我們就實現了使用模板模式的一個完整的例項。

在書本上看到這這麼一個例子:

去銀行辦理業務,如取錢,存錢或者辦卡等,基本上需要三個大的步驟(骨架)

  • 取號
  • 辦具體業務
  • 服務評介打分

然後這三個步驟就可以抽取到父類中進行定義

這裡寫圖片描述

takeNumber(取號),trabsact(具體業務),evaluate(評價),process(骨架方法)。

模板設計模式常在資料庫操作中使用,我現在使用模板模式做一個JDBC的查詢模板

public abstract class AbstractDao {

    /**
     * 查詢
     * @param sql
     * @param params
     * @return
     */
    protected Object find(String sql, Object[] params) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Object obj = null;
        try {
            conn = JDBCUtils.getConnection();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            rs = ps.executeQuery();
            while (rs.next()) {
                obj = rowMapper(rs);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.free(rs, ps, conn);
        }
        return obj;
    }
    
    protected abstract Object rowMapper(ResultSet rs) throws SQLException;
    
    //同時可以新增 insert ,update 等方法
}
public class UserDao extends AbstractDao {

    public User findUser(int userId) {
        String sql = "select * from t_user where userId = ?";
        Object[] params = new Object[] { userId };
        Object user = super.find(sql, params);
        System.out.println((User) user);
        return (User) user;
    }

    @Override
    protected Object rowMapper(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getInt("userId"));
        user.setName(rs.getString("name"));
        user.setAge(rs.getInt("age"));
        user.setSex(rs.getString("sex"));
        user.setAddress(rs.getString("address"));
        return user;
    }
}

模板模式的優點

  • 具體細節步驟實現定義在子類中,子類定義詳細處理演算法是不會改變演算法整體結構。
  • 程式碼複用的基本技術,在資料庫設計中尤為重要。
  • 存在一種反向的控制結構,通過一個父類呼叫其子類,通過子類對父類進行擴充套件增加新的行為,符合"開閉原則"。

不足

每個不同的實現都需要定義一個子類,會導致類的個數增加,系統更加龐大。