設計模式之 - 模板模式(Template Pattern)

引入:這幾天在看一本講spring源碼的書《SPRING技術內幕》裏面在講加載配置文件的時候,能夠有不一樣的加載方式,如根據文件系統目錄加載配置文件(FileSystemXmlApplicationContext),類路徑加載配置文件(ClassPathXmlApplicationContext),以及根據項目上下文目錄(XmlWebApplicationContext)加載配置文件。這個在加載的過程當中就使用了模板設計模式,因此就來學習下模板設計模式。mysql

1. 模板設計模式在書中定義:

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

  通俗點的理解就是 :完成一件事情,有固定的數個步驟,可是每一個步驟根據對象的不一樣,而實現細節不一樣;就能夠在父類中定義一個完成該事情的總方法,按照完成事件須要的步驟去調用其每一個步驟的實現方法。每一個步驟的具體實現,由子類完成。spring

2. 以下網上找到的一個模板模式的類圖:

  抽象父類(AbstractClass):實現了模板方法,定義了算法的骨架。sql

  具體類(ConcreteClass):實現抽象類中的抽象方法,即不一樣的對象的具體實現細節。數據庫

   

3. 實例說明

  來舉個例子: 好比咱們作菜能夠分爲三個步驟 (1)備料 (2)具體作菜 (3)盛菜端給客人享用,這三部就是算法的骨架 ;然而作不一樣菜須要的料,作的方法,以及如何盛裝給客人享用都是不一樣的這個就是不一樣的實現細節。設計模式

  下來咱們就代碼實現以下app

 a. 先來寫一個抽象的作菜父類:  數據庫設計

public abstract class DodishTemplate {    
    /**
     * 具體的整個過程
     */
    protected void dodish(){
        this.preparation();
        this.doing();
        this.carriedDishes();
    }
    /**
     * 備料
     */
    public abstract void preparation();
    /**
     * 作菜
     */
    public abstract void doing();
    /**
     * 上菜
     */
    public abstract void carriedDishes ();
}

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

/**
 * 西紅柿炒蛋
 * @author aries
 */
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("將炒好的西紅寺雞蛋裝入碟子裏,端給客人吃。");
    }

}
/**
 * 紅燒肉
 * @author aries
 *
 */
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("將作好的紅燒肉盛進碗裏端給客人吃。");
    }

}

  c. 在測試類中咱們來作菜:學習

public class App {
    public static void main(String[] args) {
        DodishTemplate eggsWithTomato = new EggsWithTomato();
        eggsWithTomato.dodish();
        
        System.out.println("-----------------------------");
        
        DodishTemplate bouilli = new Bouilli();
        bouilli.dodish();
    }
}

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

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

 去銀行辦業務,如取錢,存錢或者辦卡等,基本都須要三個大的步驟(骨架),首先 (1)取號 (2)辦具體業務 (3)服務評價打分,而後這三個步驟就能夠抽取到父類中進行定義,(1)取號 (3)服務打分是相同操做,能夠直接在父類總實現,而後(2)辦具體的業務各不相同須要在子類中實現。以下我畫個類圖:

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

5. 模板設計模式常在數據庫操做中使用,我如今使用模板模式作一個JDBC的查詢模板:

  (1) 抽象查詢父類 

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 等方法 }

 (2)具體的UserDao

/**
 * userDao
 * 
 * @author aries
 *
 */
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;
    }
}

 (3)以上代碼中用到的User類和JDBCUtil

public class JDBCUtils {
    private static String url = "jdbc:mysql://localhost:3306/jdbcstudy";
    private static String user = "root";
    private static String password = "123";

    private JDBCUtils() {
    }

    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
    
    public static void free(ResultSet rs, PreparedStatement ps, Connection conn){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
/**
 * 用戶類
 * 
 * @author aries
 *
 */
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    private String address;
        
    //set...get省略    
}

 如上就使用模板模式作的查詢,父類中作了算法骨架,子類中具體實現算法中的不一樣部分。

6. 模板模式的優勢

 (1)具體細節步驟實現定義在子類中,子類定義詳細處理算法是不會改變算法總體結構。

 (2)代碼複用的基本技術,在數據庫設計中尤其重要。

 (3)存在一種反向的控制結構,經過一個父類調用其子類的操做,經過子類對父類進行擴展增長新的行爲,符合「開閉原則」。

7. 不足

    每一個不一樣的實現都須要定義一個子類,會致使類的個數增長,系統更加龐大。

相關文章
相關標籤/搜索