一天一個設計模式:模板方法模式

概念:

  模板方法模式是類的行爲模式,準備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然後聲明一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。這就是模板方法模式的意思。

結構:

  模板方法模式中,抽象類負責定義整個的邏輯框架,而具體的邏輯步驟由具體的子類實現。具體的邏輯步驟稱作基本方法(primitive method),將基本方法彙總的方法稱爲模板方法(template method)

  模板方法是頂級的行爲定義,即該抽象類可以作什麼功能是由模板方法定義的,但是其中的細節則是由其子類中的基本方法實現的。

  用一句諺語表示就是:條條大路通羅馬

靜態結構圖:

抽象模板(Abstract Template)角色:

  定義了一個或多個抽象操作,以便讓子類實現,這些抽象操作稱爲基本方法,他們是頂級邏輯的組成步驟。

  定義並實現了一個模板方法,模板方法一般是一個具體實現,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲子類的實現,頂級邏輯也可能調用一些具體的方法。(不建議這樣)

具體模板(Concrete Template)角色:

  實現父類定義的一個或多個抽象方法,這些實現方法是頂級邏輯的組成步驟。

  一個抽象模板可以有多個具體模板對其實現,且其實現的細節均可不同。

代碼:

抽象模板角色類:

public abstract class AbstractTemplate {
    /**
     * 模板方法
     */
    public void templateMethod(){
        //調用基本方法
        abstractMethod();
        hookMethod();
        concreteMethod();
    }
    /**
     * 基本方法的聲明(由子類實現)
     */
    protected abstract void abstractMethod();
    /**
     * 基本方法(空方法)
     */
    protected void hookMethod(){}
    /**
     * 基本方法(已經實現)
     */
    private final void concreteMethod(){
        //業務相關的代碼
    }
}

具體模板角色類:

public class ConcreteTemplate extends AbstractTemplate{
    //基本方法的實現
    @Override
    public void abstractMethod() {
        //業務相關的代碼
    }
    //重寫父類的方法
    @Override
    public void hookMethod() {
        //業務相關的代碼
    }
}

關鍵的地方在於:子類可以置換掉父類的可變部分,但是子類卻不可以改變模板方法所代表的頂級邏輯。

模板方法模式的方法:

分爲兩大類:模板方法和基本方法

  模板方法

  一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總算法或一個總行爲的方法。

  一個抽象方法可以包含任意個模板方法,每個模板方法都可以調用任意多個具體方法。

  基本方法:

  分爲三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。

抽象方法:一個抽象方法由抽象類聲明,由具體子類實現。在Java語言裏抽象方法以abstract關鍵字標示。

具體方法:一個具體方法由抽象類聲明並實現,而子類並不實現或置換。 鉤子方法:一個鉤子方法由抽象類聲明並實現,而子類會加以擴展。通常抽象類給出的實現是一個空實現,作爲方法的默認實現。

在上面的例子中,AbstractTemplate是一個抽象類,它帶有三個方法。其中abstractMethod()是一個抽象方法,它由抽象類聲明爲抽象方法,並由子類實現;hookMethod()是一個鉤子方法,它由抽象類聲明並提供默認實現,並且由子類置換掉。concreteMethod()是一個具體方法,它由抽象類聲明並實現。

  默認鉤子方法:

  一個鉤子方法常常由抽象類給出一個空實現,作爲此方法的默認實現,這種空的鉤子方法叫做"Do Nothing Hook"。

  命名規則:

  鉤子方法的名字應該以do開始。

模板方法模式在Servlet中的應用:

  使用過Servlet的人都清楚,除了要在web.xml做相應的配置外,還需繼承一個叫HttpServlet的抽象類。HttpService類提供了一個service()方法,這個方法調用七個do方法中的一個或幾個,完成對客戶端調用的響應。這些do方法需要由HttpServlet的具體子類提供,因此這是典型的模板方法模式。下面是service()方法的源代碼:

    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {

        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            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 / 1000 * 1000)) {
                    // 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);
        }
    }

如圖所示,我們也可以對servlet的方法進行重寫。

  從上面的例子可以看出這是一個典型的模板方法模式。

HttpServlet擔任抽象模板角色

    模板方法:由service()方法擔任。

    基本方法:由doPost()、doGet()等方法擔任。

TestServlet擔任具體模板角色

TestServlet置換掉了父類HttpServlet中七個基本方法中的其中兩個,分別是doGet()和doPost()。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章