Java設(shè)計(jì)模式--模板模式

一.模板模式概念

在Java中實(shí)現(xiàn)某類事情的步驟有些是固定的卜范,有些是會發(fā)生變化的弟晚,因此出現(xiàn)了模板方法這一概念。模板模式在Java中一般才用abstract實(shí)現(xiàn),我們將固定的步驟在抽象類中實(shí)現(xiàn)调榄,將根據(jù)需要變化的部分設(shè)置為抽象方法,讓其實(shí)現(xiàn)類來根據(jù)自己的需要實(shí)現(xiàn)(必須實(shí)現(xiàn))呵扛,在標(biāo)準(zhǔn)的模板方法模式實(shí)現(xiàn)中每庆,主要是使用繼承的方式,來讓父類在運(yùn)行期間可以調(diào)用到子類的方法今穿。

二.模板模式實(shí)現(xiàn)步驟:

1.寫先出解決該類事情的一個(gè)具體例子的解決方案(也就是將這個(gè)問題特殊化缤灵,提出一種解決方案,并寫出相應(yīng)的代碼)蓝晒;
2.分析代碼凤价,把會發(fā)生變化的代碼抽取出來獨(dú)立成一個(gè)方法,把該方法描述成一個(gè)抽象的方法拔创;
3.使用final修飾模板方法利诺,防止別人重寫模板方法。
我們用一個(gè)很簡單的代碼來表述一下:

public abstract class Template {  
  
    //模板方法  
    public final void templateMethod(){  
          
        method1();  
        method2();//勾子方法  
        method3();//抽象方法  
    }  
    private void method1(){  
        System.out.println("父類實(shí)現(xiàn)業(yè)務(wù)邏輯");  
    }  
    public void method2(){  
        System.out.println("父類默認(rèn)實(shí)現(xiàn)剩燥,子類可覆蓋");  
    }  
    protected abstract void method3();//子類負(fù)責(zé)實(shí)現(xiàn)業(yè)務(wù)邏輯  
}  

父類中有三個(gè)方法慢逾,分別是method1(),method2()和method3()灭红。
method1()是私有方法侣滩,有且只能由父類實(shí)現(xiàn)邏輯,由于方法是private的变擒,所以只能父類調(diào)用君珠。
method2()是所謂的勾子方法。父類提供默認(rèn)實(shí)現(xiàn)娇斑,如果子類覺得有必要定制策添,則可以覆蓋父類的默認(rèn)實(shí)現(xiàn)。
method3()是子類必須實(shí)現(xiàn)的方法毫缆,即制定的步驟唯竹。
由此可看出,算法的流程執(zhí)行順序是由父類掌控的苦丁,子類是根據(jù)自己的需要完成自己的代碼部分來配合總體流程實(shí)現(xiàn)浸颓。
接下來用一個(gè)子類來繼承模板類。

public class TemplateImpl extends Template {  
    @Override  
    protected void method3() {  
        System.out.println("method3()在子類TemplateImpl中實(shí)現(xiàn)了!产上!");  
    }  
}  

在這里子類只覆蓋了自己必須覆蓋(模板類的abstract方法)的部分棵磷。
我們來測試一下:

Template t1 = new TemplateImpl();  
t1.templateMethod();  

//結(jié)果是:
父類實(shí)現(xiàn)業(yè)務(wù)邏輯  
父類默認(rèn)實(shí)現(xiàn),子類可覆蓋  
method3()在子類TemplateImpl中實(shí)現(xiàn)了=痢仪媒!  

可以看出這里TemplateImpl根據(jù)需要在必須實(shí)現(xiàn)的method3()中添加了自己的功能模塊,method2()方法父類已經(jīng)提供了默認(rèn)實(shí)現(xiàn)姻僧,如果子類覺得有必要更改的實(shí)現(xiàn)规丽,也可以根據(jù)自己的需要來實(shí)現(xiàn)相應(yīng)的功能模塊。

Spring--模板模式

在之前接觸spring的時(shí)候使用了JdbcTemplate方法撇贺,當(dāng)時(shí)就覺得spring中的設(shè)計(jì)模式真的是太流弊了赌莺。模板方法(template method)在spring中也是被大量使用,如:jdbcTemplate,hibernateTemplate,JndiTemplate以及一些包圍的包裝等都無疑使用了模板模式松嘶,但spring并不是單純使用了模板方法艘狭,而是在此基礎(chǔ)上做了創(chuàng)新,配合callback(回調(diào))一起使用翠订,用得極其靈活巢音。 回調(diào)模式這里不進(jìn)行講解,下一節(jié)再進(jìn)行講解尽超。

spring通過封裝JDBC API官撼,對外提供jdbcTemplate實(shí)現(xiàn)了代碼的靈活復(fù)用,大大提高了程序員的開發(fā)效率∷扑現(xiàn)在假設(shè)一下spring沒有采用模板模式會怎么樣呢傲绣?如下是一段曾經(jīng)堪稱經(jīng)典的JDBC API代碼:

public List<User> query() {    
    List<User> userList = new ArrayList<User>();  
    String sql = "select * from User";  
  
    Connection con = null;  
    PreparedStatement pst = null;  
    ResultSet rs = null;  
    try {  
        con = HsqldbUtil.getConnection();  
        pst = con.prepareStatement(sql);  
        rs = pst.executeQuery();  
  
        User user = null;  
        while (rs.next()) {  
  
            user = new User();  
            user.setId(rs.getInt("id"));  
            user.setUserName(rs.getString("user_name"));  
            user.setBirth(rs.getDate("birth"));  
            user.setCreateDate(rs.getDate("create_date"));  
            userList.add(user);  
        }  
  
  
    } catch (SQLException e) {  
        e.printStackTrace();  
    }finally{  
        if(rs != null){  
            try {  
                rs.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
        try {  
            pst.close();  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
        try {  
            if(!con.isClosed()){  
                try {  
                    con.close();  
                } catch (SQLException e) {  
                    e.printStackTrace();  
                }  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }  
          
    }  
    return userList;  
}  

可以看出在這段簡單的User表查詢代碼中,我們依次做了以下工作:
1巩踏、獲取connection
2秃诵、獲取statement
3、獲取resultset
4塞琼、遍歷resultset并封裝成集合
5菠净、依次關(guān)閉connection,statement,resultset,而且還要考慮各種異常
6彪杉、.....
這里只是查詢了一個(gè)User表所需的代碼毅往,如果需要查詢其他表呢?當(dāng)然是也要不斷地做重復(fù)的工作......可想而知需要多少冗余的代碼來完成相同的工作在讶。

此時(shí)模板模式就出現(xiàn)了:
通過觀察我們發(fā)現(xiàn)上面步驟中大多數(shù)都是重復(fù)的煞抬,可復(fù)用的,只有在遍歷ResultSet并封裝成集合的這一步驟是需要根據(jù)靈活調(diào)整的构哺,因?yàn)槊繌埍矶加成洳煌膉ava bean。這部分代碼是沒有辦法復(fù)用的。接下來讓我們用一個(gè)抽象的父類將公共部分封裝一下:

public abstract class JdbcTemplate {  
  
    //template method  
    public final Object execute(String sql) throws SQLException{  
          
        Connection con = HsqldbUtil.getConnection();  
        Statement stmt = null;  
        try {  
   
            stmt = con.createStatement();  
            ResultSet rs = stmt.executeQuery(sql);  
            Object result = doInStatement(rs);//abstract method   
            return result;  
        }  
        catch (SQLException ex) {  
             ex.printStackTrace();  
             throw ex;  
        }  
        finally {  
   
            try {  
                stmt.close();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
            try {  
                if(!con.isClosed()){  
                    try {  
                        con.close();  
                    } catch (SQLException e) {  
                        e.printStackTrace();  
                    }  
                }  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
              
        }  
    }  
      
    //implements in subclass  
    protected abstract Object doInStatement(ResultSet rs);  
}

在上面的抽象類中曙强,封裝了JDBC API的主要流程残拐,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,交由子類實(shí)現(xiàn)碟嘴。
接下來我們定義一個(gè)子類溪食,并繼承上面的父類:

public class JdbcTemplateUserImpl extends JdbcTemplate {  
  
    @Override  
    protected Object doInStatement(ResultSet rs) {  
        List<User> userList = new ArrayList<User>();  
          
        try {  
            User user = null;  
            while (rs.next()) {  
  
                user = new User();  
                user.setId(rs.getInt("id"));  
                user.setUserName(rs.getString("user_name"));  
                user.setBirth(rs.getDate("birth"));  
                user.setCreateDate(rs.getDate("create_date"));  
                userList.add(user);  
            }  
            return userList;  
        } catch (SQLException e) {  
            e.printStackTrace();  
            return null;  
        }  
    }  
}  

我們在子類的doInStatement()方法中遍歷并封裝了結(jié)果集返回。
接下來我們只需要來調(diào)用方法即可:

String sql = "select * from User";  
JdbcTemplate jt = new JdbcTemplateUserImpl();  
List<User> userList = (List<User>) jt.execute(sql);  

至此娜扇,模板模式就成功了應(yīng)用到了JDBC API當(dāng)中错沃,這里只是一張表的查詢,查詢過程中代碼只需要實(shí)現(xiàn)查詢結(jié)果集的封裝即可雀瓢,不需要在新的查詢當(dāng)中再次編寫關(guān)于創(chuàng)建連接枢析、捕獲異常、釋放連接這些公共的代碼刃麸,通過采用模板模式極大地提高了代碼復(fù)用性醒叁。

這里我們通過代碼講解spring中的JdbcTemplate。根據(jù)前邊模板模式的使用步驟:先將固定化的流程:數(shù)據(jù)庫連接的獲取泊业、連接的關(guān)閉等功能模塊實(shí)現(xiàn)把沼。然后將變化的部分(結(jié)果集的封裝)通過抽象方法交給子類或者回調(diào)函數(shù)來實(shí)現(xiàn)。

模板模式學(xué)習(xí)過程中參照了一些前人的文章:
https://blog.csdn.net/zhang23242/article/details/9305925
https://blog.csdn.net/jpzhu16/article/details/50888435

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吁伺,一起剝皮案震驚了整個(gè)濱河市饮睬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌篮奄,老刑警劉巖捆愁,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宦搬,居然都是意外死亡牙瓢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門间校,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矾克,“玉大人,你說我怎么就攤上這事憔足⌒哺剑” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵滓彰,是天一觀的道長控妻。 經(jīng)常有香客問我,道長揭绑,這世上最難降的妖魔是什么弓候? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任郎哭,我火速辦了婚禮,結(jié)果婚禮上菇存,老公的妹妹穿的比我還像新娘夸研。我一直安慰自己,他們只是感情好依鸥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布亥至。 她就那樣靜靜地躺著,像睡著了一般贱迟。 火紅的嫁衣襯著肌膚如雪姐扮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天衣吠,我揣著相機(jī)與錄音茶敏,去河邊找鬼。 笑死蒸播,一個(gè)胖子當(dāng)著我的面吹牛睡榆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袍榆,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼胀屿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了包雀?” 一聲冷哼從身側(cè)響起宿崭,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎才写,沒想到半個(gè)月后葡兑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赞草,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年讹堤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厨疙。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洲守,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出沾凄,到底是詐尸還是另有隱情梗醇,我是刑警寧澤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布撒蟀,位于F島的核電站叙谨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏保屯。R本人自食惡果不足惜手负,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一涤垫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧虫溜,春花似錦雹姊、人聲如沸股缸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敦姻。三九已至瘾境,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間镰惦,已是汗流浹背迷守。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旺入,地道東北人兑凿。 一個(gè)月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像茵瘾,于是被迫代替她去往敵國和親礼华。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355