<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          設(shè)計(jì)模式 | 五分鐘學(xué)【模板方法】模式

          共 10131字,需瀏覽 21分鐘

           ·

          2021-01-25 14:10

          概述

          模板模式就是定義一個(gè)操作中的算法骨架,然后將一些步驟延遲到子類中。模板方法使得子類在不改變算法的結(jié)構(gòu)即可重定義該算法的某些步驟。

          點(diǎn)擊這里:一份10萬字的面試小抄

          使用場景

          喝茶水

          我們都知道泡茶基本步驟(算法骨架)有:

          燒水、泡茶、喝茶水。

          整個(gè)過程中很關(guān)鍵的步驟是泡茶,泡茶需要跑什么茶呢?泡多久?(留給子類自己去實(shí)現(xiàn))。

          API

          寫過API接口的碼友們都知道,寫API一般有四個(gè)步驟:

          參數(shù)解析、參數(shù)校驗(yàn)、處理業(yè)務(wù)、組織返回參數(shù)。

          把請求參數(shù)解析成該業(yè)務(wù)的請求參數(shù)json解析成實(shí)體類;參數(shù)校驗(yàn),您可以使用通用的方式就是判斷參數(shù)是否為空,也可以自己定義特殊的校驗(yàn)方式;處理業(yè)務(wù)一般每個(gè)接口都是不一樣的,基本上都是自己去實(shí)現(xiàn);至于返回參數(shù),可能您得根據(jù)該API接口業(yè)務(wù)來返回。

          支付訂單

          做過支付相關(guān)的系統(tǒng)的人都清楚,支付訂單大致分這三個(gè)步驟:

          組織請求銀行或者第三方支付公司的請求參數(shù)、發(fā)起支付、處理返回結(jié)果。

          以上三個(gè)場景中的步驟就是算法骨架,至于每個(gè)步驟可能每個(gè)人喝茶偏好不一樣,API接口業(yè)務(wù)不一樣、銀行或者第三方支付的支付處理不一樣,可能需要自己做特殊的處理。

          場景現(xiàn)實(shí)

          實(shí)現(xiàn)一個(gè)API接口

          算法類
          package?com.tian.springbootdemo.controller;
          import?com.tian.springbootdemo.rep.Result;
          /**
          ?*?@auther:?老田
          ?*?@Description:?模板類
          ?*/

          public?abstract?class?AbstractTemplate?{

          ????/**
          ?????*?算法骨架
          ?????*/

          ????public?Result?execute()?{
          ????????//第一步:解析參數(shù)
          ????????parseRequestParameters();
          ????????//第二步:校驗(yàn)參數(shù)
          ????????checkRequestParameters();
          ????????//第三步:業(yè)務(wù)處理
          ????????Object?data=?doBusiness();
          ????????//第四步:組織返回參數(shù)
          ????????return?assembleResponseParameters(data);
          ????}

          ????/**
          ?????*?解析參數(shù)
          ?????*/

          ????public?abstract?void?parseRequestParameters();

          ????/**
          ?????*?校驗(yàn)參數(shù)
          ?????*/

          ????public?abstract?void?checkRequestParameters();

          ????/**
          ?????*?業(yè)務(wù)處理
          ?????*/

          ????public?abstract?Object?doBusiness();

          ????/**
          ?????*?組織返回參數(shù)
          ?????*/

          ????public?abstract?Result?assembleResponseParameters(Object?object);
          }
          實(shí)現(xiàn)類一
          import?com.tian.springbootdemo.rep.Result;
          import?org.springframework.stereotype.Controller;
          import?org.springframework.web.bind.annotation.RequestMapping;
          import?org.springframework.web.bind.annotation.RequestMethod;
          import?org.springframework.web.bind.annotation.ResponseBody;

          /**
          ?*?@auther:?老田
          ?*?@Description:?api接口
          ?*/

          @RequestMapping("/api")
          @Controller
          public?class?MyApiController?extends?AbstractTemplate?{

          ????@RequestMapping(value?=?"/users",?method?=?RequestMethod.POST)
          ????@ResponseBody
          ????@Override
          ????public?Result?execute()?{
          ????????return?super.execute();
          ????}

          ????@Override
          ????public?void?parseRequestParameters()?{
          ????????System.out.println("*****解析參數(shù)*****");
          ????}

          ????@Override
          ????public?void?checkRequestParameters()?{
          ????????System.out.println("*****校驗(yàn)參數(shù)*****");
          ????}

          ????@Override
          ????public?Object?doBusiness()?{
          ????????System.out.println("*****處理業(yè)務(wù)*****");
          ????????//?TODO:?2018/11/17?調(diào)用service處理業(yè)務(wù)
          ????????User?user?=?new?User();
          ????????user.setName("小田哥");
          ????????user.setId(1);
          ????????user.setAge(20);
          ????????user.setSex("man");
          ????????return?user;
          ????}

          ????@Override
          ????public?Result?assembleResponseParameters(Object?object)?{
          ????????System.out.println("*****返回參數(shù)*****");
          ????????Result?result?=?new?Result("200",?"處理成功");
          ????????result.setData(object);
          ????????return?result;
          ????}
          }
          實(shí)現(xiàn)類二
          import?com.tian.springbootdemo.dao.domain.User;
          import?com.tian.springbootdemo.rep.Result;
          import?org.springframework.web.bind.annotation.*;

          /**
          ?*?@auther:?老田
          ?*?@Description:?api接口
          ?*/

          @RequestMapping("/api")
          @RestController
          public?class?LoginController?extends?AbstractTemplate?{
          ????@PostMapping(value?=?"/login")
          ????@Override
          ????public?Result?execute()?{
          ????????return?super.execute();
          ????}
          ????@Override
          ????public?void?parseRequestParameters()?{
          ????????System.out.println("解析登錄參數(shù)");
          ????}

          ????@Override
          ????public?void?checkRequestParameters()?{
          ????????System.out.println("校驗(yàn)登錄用戶名是否為空,密碼是否為空");
          ????}

          ????@Override
          ????public?Object?doBusiness()?{
          ????????System.out.println("通過用戶名查詢是否存在此用戶");
          ????????System.out.println("校驗(yàn)用戶密碼是否正確");
          ????????System.out.println("登錄成功");
          ????????User?user?=?new?User();
          ????????user.setName("小田哥");
          ????????user.setId(1);
          ????????user.setAge(20);
          ????????user.setSex("man");
          ????????return?user;
          ????}

          ????@Override
          ????public?Result?assembleResponseParameters(Object?object)?{
          ????????System.out.println("*****返回參數(shù)*****");
          ????????Result?result?=?new?Result("200",?"登錄成功");
          ????????result.setData(object);
          ????????return?result;
          ????}
          }
          相關(guān)類
          /**
          ?*?@auther:?老田
          ?*?@Description:?返回信息
          ?*/

          public?class?Result?{
          ????//返回碼
          ????private?String?responseCode;
          ????//描述
          ????private?String?message;
          ????//數(shù)據(jù)
          ????private?Object?data;

          ????public?Result()?{
          ????}

          ????public?Result(String?responseCode,?String?message)?{
          ????????this.responseCode?=?responseCode;
          ????????this.message?=?message;
          ????}

          ????public?Result(String?responseCode,?String?message,?Object?data)?{
          ????????this.responseCode?=?responseCode;
          ????????this.message?=?message;
          ????????this.data?=?data;
          ????}

          ????public?String?getResponseCode()?{
          ????????return?responseCode;
          ????}

          ????public?void?setResponseCode(String?responseCode)?{
          ????????this.responseCode?=?responseCode;
          ????}

          ????public?String?getMessage()?{
          ????????return?message;
          ????}

          ????public?void?setMessage(String?message)?{
          ????????this.message?=?message;
          ????}

          ????public?Object?getData()?{
          ????????return?data;
          ????}

          ????public?void?setData(Object?data)?{
          ????????this.data?=?data;
          ????}
          }
          import?java.io.Serializable;

          /**
          ?*?@auther:?老田
          ?*?@Description:?數(shù)據(jù)
          ?*/

          public?class?User?implements?Serializable?{
          ????//id
          ????private?Integer?id;
          ????//用戶姓名
          ????private?String?name;
          ????//性別
          ????private?String?sex;
          ????//年齡
          ????private?int?age;

          ????public?User()?{
          ????}

          ????public?User(Integer?id,?String?name,?String?sex,?int?age)?{
          ????????this.id?=?id;
          ????????this.name?=?name;
          ????????this.sex?=?sex;
          ????????this.age?=?age;
          ????}

          ????public?Integer?getId()?{
          ????????return?id;
          ????}

          ????public?void?setId(Integer?id)?{
          ????????this.id?=?id;
          ????}

          ????public?String?getName()?{
          ????????return?name;
          ????}

          ????public?void?setName(String?name)?{
          ????????this.name?=?name;
          ????}

          ????public?String?getSex()?{
          ????????return?sex;
          ????}

          ????public?void?setSex(String?sex)?{
          ????????this.sex?=?sex;
          ????}

          ????public?int?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(int?age)?{
          ????????this.age?=?age;
          ????}
          }
          測試

          這里使用的是ideaTools下面的REST Client進(jìn)行接口測試:

          enter image description here
          enter image description here

          再看看控制臺Console打印出來的信息:

          enter image description here

          enter image description here

          這樣我們就把模板設(shè)計(jì)模式應(yīng)用到我們的具體代碼里了,同樣的我們也可以實(shí)現(xiàn)其他API的實(shí)現(xiàn)類。

          另外,參數(shù)校驗(yàn)也可以在 AbstractTemplate ?中實(shí)現(xiàn)一個(gè) default ? 的方式,比如說:校驗(yàn)參數(shù)是否為空,但是子類也可以重寫這個(gè)方法,自己做一個(gè)特殊的校驗(yàn);比如說:如果參數(shù)中有手機(jī)號碼,那么我們不僅要校驗(yàn)手機(jī)號是否為空,還可以校驗(yàn)這個(gè)手機(jī)號碼是不是11位,是否合法的校驗(yàn)等等。

          模板模式優(yōu)缺點(diǎn)

          優(yōu)點(diǎn)
          • 提高代碼的復(fù)用性,將相同部分的代碼放到抽象類里;

          • 提高拓展性,將不同的放到不同的實(shí)現(xiàn)類里,通過實(shí)現(xiàn)類的擴(kuò)展增加一些自己需要的行為;

          • 實(shí)現(xiàn)反向控制,通過一個(gè)父類調(diào)用實(shí)現(xiàn)類的操作,通過對實(shí)現(xiàn)類的擴(kuò)展增加新行為,實(shí)現(xiàn)反向控制。

          缺點(diǎn)
          • 因?yàn)橐肓顺橄箢?,每個(gè)不同的實(shí)現(xiàn)都需要一個(gè)子類來現(xiàn)實(shí),這樣會導(dǎo)致類的數(shù)量增多,從而導(dǎo)致系統(tǒng)實(shí)現(xiàn)的復(fù)雜度。

          大佬們在框架里是怎么使用的?

          Spring中

          AbstractApplicationContext 中的refreash方法就是模板方法,源碼為:

          @Override
          public?void?refresh()?throws?BeansException,?IllegalStateException?{
          ????????synchronized?(this.startupShutdownMonitor)?{?
          ????????????//調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時(shí)時(shí)間,
          ????????????//同時(shí)給容器設(shè)置同步標(biāo)識
          ????????????prepareRefresh();
          ????????????//告訴子類啟動refreshBeanFactory()方法,
          ????????????//Bean定義資源文件的載入從
          ????????????//子類的refreshBeanFactory()方法啟動
          ????????????ConfigurableListableBeanFactory?beanFactory?=?obtainFreshBeanFactory();
          ????????????//為BeanFactory配置容器特性,例如類加載器、事件處理器等
          ????????????prepareBeanFactory(beanFactory);
          ????????????try?{?
          ????????????????//為容器的某些子類指定特殊的BeanPost事件處理器
          ????????????????//-----子類實(shí)現(xiàn)
          ????????????????postProcessBeanFactory(beanFactory);
          ????????????????//調(diào)用所有注冊的BeanFactoryPostProcessor的Bean
          ????????????????invokeBeanFactoryPostProcessors(beanFactory);
          ????????????????//為BeanFactory注冊BeanPost事件處理器.
          ????????????????//BeanPostProcessor是Bean后置處理器,
          ????????????????//用于監(jiān)聽容器觸發(fā)的事件
          ????????????????registerBeanPostProcessors(beanFactory);
          ????????????????//初始化信息源,和國際化相關(guān).
          ????????????????initMessageSource();
          ????????????????//初始化容器事件傳播器.
          ????????????????initApplicationEventMulticaster();
          ????????????????//調(diào)用子類的某些特殊Bean初始化方法
          ????????????????//-----子類實(shí)現(xiàn)
          ????????????????onRefresh();
          ????????????????//為事件傳播器注冊事件監(jiān)聽器.
          ????????????????registerListeners();
          ????????????????//初始化所有剩余的單例Bean
          ????????????????finishBeanFactoryInitialization(beanFactory);
          ????????????????//初始化容器的生命周期事件處理器,
          ????????????????//并發(fā)布容器的生命周期事件
          ????????????????finishRefresh();
          ????????????????//.....

          該方法就是上下文啟動模板方法。這就是模板模式在Spring中應(yīng)用場景之一。

          Mybatis中

          BaseExecutor中的update方法就是一個(gè)模板方法

          ?/**
          ?????*?SqlSession.update/insert/delete會調(diào)用此方法
          ?????*?模板方法
          ?????*/

          ????@Override
          ????public?int?update(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
          ?????????ErrorContext.instance().resource(ms.getResource()).activity("executing?an????????????????update").object(ms.getId());
          ????????if?(closed)?{
          ????????????throw?new?ExecutorException("Executor?was?closed.");
          ????????}
          ????????//先清局部緩存,再更新,如何更新交由子類,
          ????????//模板方法模式
          ????????clearLocalCache();
          ????????//由子類實(shí)現(xiàn)(鉤子方法)
          ????????return?doUpdate(ms,?parameter);
          ????}

          BaseExecutor里只是定義了方法,但是實(shí)現(xiàn)是在子類里

          //更新?
          protected?abstract?int?doUpdate(MappedStatement?ms,?Object?parameter)
          ?????????????????????????????????????????????????????throws?SQLException
          ;
          //查詢
          protected?abstract??List?doQuery(MappedStatement?ms,?Object?parameter,?RowBounds?
          ????????????????????rowBounds,?ResultHandler?resultHandler,?BoundSql?boundSql)

          ???????????????????throws?SQLException
          ;
          ?//...do開頭的方法都是交給具體子類自己去實(shí)現(xiàn)

          BaseExecutor的實(shí)現(xiàn)類如下:

          enter image description here

          實(shí)現(xiàn)類SimpleExecutor中的doUpdate方法的實(shí)現(xiàn)

          @Override
          public?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
          ????Statement?stmt?=?null;
          ????try?{
          ??????Configuration?configuration?=?ms.getConfiguration();
          ??????//新建一個(gè)StatementHandler
          ??????//這里看到ResultHandler傳入的是null
          ??????StatementHandler?handler?=?configuration.newStatementHandler(
          ??????????this,?ms,?parameter,??????????RowBounds.DEFAULT,?null,?null);
          ??????//準(zhǔn)備語句
          ??????stmt?=?prepareStatement(handler,?ms.getStatementLog());
          ??????//StatementHandler.update
          ??????return?handler.update(stmt);
          ????}?finally?{
          ??????closeStatement(stmt);
          ????}
          }

          實(shí)現(xiàn)類ReuseExecutor中的doUpdate方法的實(shí)現(xiàn)

          @Override
          ??public?int?doUpdate(MappedStatement?ms,?Object?parameter)?throws?SQLException?{
          ????Configuration?configuration?=?ms.getConfiguration();
          ?????//和SimpleExecutor一樣,
          ?????//新建一個(gè)StatementHandler
          ?????//這里看到ResultHandler傳入的是null
          ?????StatementHandler?handler?=?configuration.newStatementHandler(
          ?????????????this,?ms,?parameter,???????RowBounds.DEFAULT,?null,?null);
          ?????//準(zhǔn)備語句
          ?????Statement?stmt?=?prepareStatement(handler,?ms.getStatementLog());
          ?????return?handler.update(stmt);
          ??}

          這就是Mybatis中的模板方法模式的經(jīng)典應(yīng)用。

          總結(jié)

          模板方法模式就是定義了一個(gè)算法骨架,然后每個(gè)實(shí)現(xiàn)類自己去實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。在Spring、Mybatis、Dubbo等框架中有很好實(shí)現(xiàn)案例。相對來說模板方法模式是算比較簡單的哈,在面試中也能和面試官扯一會兒了。

          推薦閱讀:
          20張圖帶你到HBase的世界遨游
          這幾道tomcat面試題,最后兩道難倒我了
          由淺入深逐步了解 Synchronized
          我竟然被“雙親委派”給虐了
          面試官留步!聽我跟你侃會兒Docker原理
          順豐快遞:請簽收MySQL靈魂十連

          關(guān)聯(lián)網(wǎng)構(gòu),價(jià)。


          瀏覽 11
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一道本一区二区三区 | 人人爽,人人操 | 欧美一级大香蕉 | av在线免费网站 a片黄色成人电影 | 夜福利导航|