模板方法設(shè)計模式(Template Method)


最易懂設(shè)計模式解析
適配器設(shè)計模式
Mybatis代理設(shè)計模式
Mybatis多級代理

1. 認識模板方法模式

1.1 模式定義

定義一個操作算法中的框架铺根,而將這些步驟延遲加載到子類中。

它的本質(zhì)就是固定算法框架乔宿。

1.2 解決何種問題

讓父類控制子類方法的調(diào)用順序

模板方法模式使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟位迂。

1.3 模式好處

開發(fā)人員在開發(fā)時,只需要考慮方法的實現(xiàn)详瑞。不需要考慮方法在何種情況下被調(diào)用掂林。實現(xiàn)代碼復(fù)用。

1.4 模式適合場景

  • 一次性實現(xiàn)一個算法的不變部分坝橡,并將可變的行為留給子類來實現(xiàn)泻帮。
  • 各子類中公共的行為應(yīng)被提取出來并集中到一個公共父類中以避免代碼重復(fù)。
  • 需要通過子類來決定父類算法中某個步驟是否執(zhí)行计寇,實現(xiàn)子類對父類的反向控制锣杂。

2. 模式結(jié)構(gòu)與實例講解

2.1模式結(jié)構(gòu)

模板方法模式結(jié)構(gòu)圖

如模板方法模式結(jié)構(gòu)圖所知脂倦,有兩個類:

  • AblstractClass(抽象類):在抽象類中定義了一系列的操作PrimitiveOperation,每個操作可以使具體的元莫,也可以是抽象的赖阻,每個操作對應(yīng)一個算法的步驟,在子類中可以重新定義或?qū)崿F(xiàn)這些步驟踱蠢。TmplateMethod()這個方法用于定義一個算法結(jié)構(gòu)火欧,模板方法不僅可以調(diào)用在抽象類中實現(xiàn)的基本方法,也可以調(diào)用在抽象類的子類中實現(xiàn)的基本方法朽基,還可以調(diào)用其他對象中的方法布隔。
  • ConcreteClass(具體子類):用于實現(xiàn)在父類中聲明的抽象基本操作,也可以覆蓋在父類中已經(jīng)實現(xiàn)的具體基本操作稼虎。

2.2 實例講解

創(chuàng)建一個抽象模板結(jié)構(gòu)(AblstractClass)好父親

public abstract class AblstractClass {

    //模板方法用來控制子類的順序 要想有人生必須按老爸的人生順序來  
    //聲明final不讓子類覆蓋這個方法谎势,防止改變?nèi)松樞?    public final void 人生(){
        學(xué)習(xí)();
        工作();
        愛情();
    }
    
    //家里窮更得用工學(xué)習(xí)
    public void 學(xué)習(xí)(){
        System.out.println("每天晚上趴在鄰居窗上學(xué)習(xí)");
    }
    
    //工作必須穩(wěn)定
    public void 工作(){       
        System.out.println("從一而終");
    }
    
    //戀愛自由  讓兒子自由戀去
    public abstract void 愛情();

}

創(chuàng)建一個具體模板(ConcreteClass)好兒子

public class ConcreteClass extends AblstractClass {
    
    //兒子不認可父親的學(xué)習(xí)方法  考高分影響同學(xué)關(guān)系
    @Override
    public void 學(xué)習(xí)() {
        System.out.println("60分萬歲...");
    }
    
    //父親給我愛情自由  一定好好談戀愛
    @Override
    public void 愛情() {
        System.out.println("膚白貌美大長腿...");
    }

}

調(diào)用他們的人生

public class TestMain {

    public static void main(String[] args) {
         ConcreteClass cs  = new ConcreteClass();
         cs.人生();
    }

}

結(jié)果輸出

60分萬歲...
從一而終
膚白貌美大長腿...

3. 模式在Servlet中的應(yīng)用

3.1 自己實現(xiàn)一下

瀏覽器向服務(wù)端發(fā)送一個請求萤衰,常用請求方式有兩種,get請求和post請求仁堪,這兩種請求方式會導(dǎo)致請求參數(shù)在請求協(xié)議包(Http包)中的位置是不一樣的沉眶,那么請求協(xié)議包中不同的內(nèi)容到達服務(wù)端之后會有不同的對象進行處理打却,如請求頭的內(nèi)容由tomcat負責,請求體中的內(nèi)容由request負責谎倔,所以此時柳击,開發(fā)人員在拿到service()方法后考慮到它可以接受所有請求方式,因此會針對不同的請求方式封裝不同的請求方法片习。

建一個OneServlet 繼承GenericServlet捌肴,實現(xiàn)service()方法,需要重寫里面的doPost和doGet方法藕咏。

public class OneServlet extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse arg1) throws ServletException, IOException {
        
        
                //1.從協(xié)議包【請求行】中來讀取瀏覽器發(fā)送的請求方式
                HttpServletRequest request = (HttpServletRequest)req;//一般來說由父類修飾的對象由子類來修飾對象状知,目的就是功能擴充
                String method = request.getMethod();//POST  GET
                if("GET".equals(method)){
                    doGet(req, arg1);
                }else if("POST".equals(method)){
                    doPost(req, arg1);
                }    
    }
    //處理瀏覽器發(fā)送的post請求
    public void doPost(ServletRequest arg0, ServletResponse arg1){
          //這里面是doPost封裝好的方法
        
          System.out.println("doPost is run....");
    }
    
    //處理瀏覽器發(fā)送的get請求
    public void doGet(ServletRequest arg0, ServletResponse arg1){
          //這里面是doPost封裝好的方法
        
          System.out.println("doGet is run....");
    }
    
}

現(xiàn)在開發(fā)人員面臨的是,即需要做方法的實現(xiàn)孽查,有需要考慮service()方法在何時調(diào)用饥悴。在實際開發(fā)過程中service()方法里面是一段重復(fù)性的代碼,所有的servlet類實現(xiàn)中都需要寫這么一段重復(fù)性的代碼盲再,這樣重復(fù)的開發(fā)既增加工作量西设,又顯得代碼臃腫,降低了系統(tǒng)耦合度答朋。模板方法設(shè)計模式就是來解決這個問題的贷揽。下面看一下怎么解決。

建立MyHttpServlet類(就是模板方法設(shè)計模式中的父類)绿映,繼承GenericServlet類擒滑。

public  class MyHttpServlet extends GenericServlet {

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        //控制子類中的doGet和doPost方法
        //1.從協(xié)議包【請求行】來讀取瀏覽器發(fā)送的請求方式
        HttpServletRequest request = (HttpServletRequest)req;
        String method = request.getMethod();//POST  GET
        if("GET".equals(method)){
            doGet(req, res);//this.doGet()
        }else if("POST".equals(method)){
            doPost(req, res);
        }
    }
    
    public  void doPost(ServletRequest arg0, ServletResponse arg1){
        
    }
    
    public void doGet(ServletRequest arg0, ServletResponse arg1){
         
    }
    
}

建立TwoServlet類,此時此刻開發(fā)人員不用去考慮何時調(diào)用doGet方法腐晾。當調(diào)用TwoServlet類的時候,tomcat一定是調(diào)用它的service()方法丐一。

/**
 * Servlet implementation class TwoServlet
 */
public class TwoServlet extends MyHttpServlet {
    
    //選擇是接受doGet方法還是doPost方法
    
    @Override
    public void doGet(ServletRequest arg0, ServletResponse arg1) {
          System.out.println("ThreeServlet doGet is run...");
    }
    
}

測試代碼localhost:8080/.../...

ThreeServlet doGet is run...

3.2 看HttpServlet源碼

HttpServlet也是繼承了GenericServlet 藻糖,跟蹤找到Service()方法,發(fā)現(xiàn)有兩個service()方法库车。

    //這個方法是從它的父類GenericServlet繼承過來的
    @Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {   
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }
        //分別對請求對象和響應(yīng)對象做了類型強轉(zhuǎn)巨柒。
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);//調(diào)用的是自己聲明的service方法,重載柠衍。
    }
}

進入到自己聲明的service()方法

  protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();//讀取請求方式

        if (method.equals(METHOD_GET)) {//根據(jù)請求方式調(diào)用對應(yīng)方法
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

發(fā)現(xiàn)service方法沒有使用final洋满,這是因為如果使用final修飾,就徹底斷絕了我們下游開發(fā)人員的開發(fā)珍坊,這樣是降低了系統(tǒng)的靈活度牺勾。

設(shè)計模式是問題解決思想(辦法),沒有固定的命令搭配 阵漏。
如果我們自己可以有這樣一些解決辦法驻民,那就是好的設(shè)計模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末履怯,一起剝皮案震驚了整個濱河市回还,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叹洲,老刑警劉巖柠硕,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異运提,居然都是意外死亡蝗柔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門糙捺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诫咱,“玉大人,你說我怎么就攤上這事洪灯】茬裕” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵签钩,是天一觀的道長掏呼。 經(jīng)常有香客問我,道長铅檩,這世上最難降的妖魔是什么憎夷? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮昧旨,結(jié)果婚禮上拾给,老公的妹妹穿的比我還像新娘祥得。我一直安慰自己,他們只是感情好蒋得,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布级及。 她就那樣靜靜地躺著,像睡著了一般额衙。 火紅的嫁衣襯著肌膚如雪饮焦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天窍侧,我揣著相機與錄音县踢,去河邊找鬼。 笑死伟件,一個胖子當著我的面吹牛硼啤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播斧账,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼丙曙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了其骄?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤扯旷,失蹤者是張志新(化名)和其女友劉穎拯爽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钧忽,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡毯炮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耸黑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桃煎。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖大刊,靈堂內(nèi)的尸體忽然破棺而出为迈,到底是詐尸還是另有隱情,我是刑警寧澤缺菌,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布葫辐,位于F島的核電站,受9級特大地震影響伴郁,放射性物質(zhì)發(fā)生泄漏耿战。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一焊傅、第九天 我趴在偏房一處隱蔽的房頂上張望剂陡。 院中可真熱鬧狈涮,春花似錦、人聲如沸鸭栖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纤泵。三九已至骆姐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捏题,已是汗流浹背玻褪。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留公荧,地道東北人带射。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像循狰,于是被迫代替她去往敵國和親窟社。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容