模板方法模式是類的行為模式。准備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然后聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩余的邏輯有不同的實現。這就是模板方法模式的用意。
模板方法模式中的方法
模板方法中的方法可以分為兩大類:模板方法和基本方法。
模板方法
一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總算法或一個總行為的方法。
一個抽象類可以有任意多個模板方法,而不限於一個。每一個模板方法都可以調用任意多個具體方法。
基本方法
基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鈎子方法(Hook Method)。
● 抽象方法:一個抽象方法由抽象類聲明,由具體子類實現。在Java語言里抽象方法以abstract關鍵字標示。
● 具體方法:一個具體方法由抽象類聲明並實現,而子類並不實現或置換。
● 鈎子方法:一個鈎子方法由抽象類聲明並實現,而子類會加以擴展。通常抽象類給出的實現是一個空實現,作為方法的默認實現。
默認鈎子方法
一個鈎子方法常常由抽象類給出一個空實現作為此方法的默認實現。這種空的鈎子方法叫做“Do Nothing Hook”。顯然,這種默認鈎子方法在缺省適配模式里面已經見過了,一個缺省適配模式講的是一個類為一個接口提供一個默認的空實現,從而使得缺省適配類的子類不必像實現接口那樣必須給出所有方法的實現,因為通常一個具體類並不需要所有的方法。
命名規則
命名規則是設計師之間賴以溝通的管道之一,使用恰當的命名規則可以幫助不同設計師之間的溝通。
鈎子方法的名字應當以do開始,這是熟悉設計模式的Java開發人員的標准做法。在上面的例子中,鈎子方法hookMethod()應當以do開頭;在HttpServlet類中,也遵從這一命名規則,如doGet()、doPost()等方法。
模板方法模式在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); } }
當然,這個service()方法也可以被子類置換掉。
下面給出一個簡單的Servlet例子:
從上面的類圖可以看出,TestServlet類是HttpServlet類的子類,並且置換掉了父類的兩個方法:doGet()和doPost()。
public class TestServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("using the GET method"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("using the POST method"); } }
從上面的例子可以看出這是一個典型的模板方法模式。
HttpServlet擔任抽象模板角色
模板方法:由service()方法擔任。
基本方法:由doPost()、doGet()等方法擔任。
TestServlet擔任具體模板角色
TestServlet置換掉了父類HttpServlet中七個基本方法中的其中兩個,分別是doGet()和doPost()。