本篇文章主要介紹模板方法模式。
模板方法模式:模板方法模式是類的行為模式。准備一個抽象類,將部分邏輯以具體方法以及具體構造函數的形式實現,然后聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而度剩余的邏輯有不同的實現。這就是模板方法模式的用意。
我們先看下結構圖。
AbstractClass是抽象類,其實也就是一抽象模板,定義並實現了一個模板方法。這個模板方法一般是一個具體方法,它給出一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能調用一些具體方法。
下面我們以大話設計模式一書中的試卷例子來簡單實現以下模板方法模式。
首先是試卷父類。
package com.lwx.template_method; /** * Created with IntelliJ IDEA. * Description: 考題試卷 * User: lwx * Date: 2019-03-11 * Time: 22:39 */ public abstract class TestPaper { private void testQuestion1(){ System.out.println("楊過得到,后來給了郭靖,煉成倚天劍、屠龍刀的玄鐵可能是:A.球磨鑄鐵 B.馬口鐵 C.高速合金鋼 D。碳素纖維"); System.out.println("學生答案:" + answer1()); } private void testQuestion2(){ System.out.println("楊過、程英、陸無雙鏟除了情花造成:A.使這種植物不再害人 B.使一種珍稀物種滅絕 C.破壞了那個生物圈的生態平衡 D.造成該地區沙漠化"); System.out.println("學生答案:" + answer2()); } private void testQuestion3(){ System.out.println("藍鳳凰致使華山師徒、核谷六仙嘔吐不止,如果你是大夫,會給他們開什么葯:A.阿匹斯林 B.牛黃解毒片 " + "C.氟哌酸 D.讓他們喝大量的生牛奶 E.以上全不對"); System.out.println("學生答案:" + answer3()); } public void answerQuestion() { testQuestion1(); testQuestion2(); testQuestion3(); } abstract String answer1(); abstract String answer2(); abstract String answer3(); }
將答案寫成抽象方法,讓子類去實現。
然后看下學生A子類。
package com.lwx.template_method; /** * Created with IntelliJ IDEA. * Description: * User: lwx * Date: 2019-03-11 * Time: 22:49 */ public class StudentAPaper extends TestPaper { @Override String answer1() { return "B"; } @Override String answer2() { return "A"; } @Override String answer3() { return "C"; } }
只要繼承試卷類,重寫下答案方法即可,學生B也一樣。
package com.lwx.template_method; /** * Created with IntelliJ IDEA. * Description: * User: lwx * Date: 2019-03-11 * Time: 22:49 */ public class StudentBPaper extends TestPaper { @Override String answer1() { return "C"; } @Override String answer2() { return "B"; } @Override String answer3() { return "A"; } }
然后我們看下測試類和運行結果。
package com.lwx.template_method; /** * Created with IntelliJ IDEA. * Description: * User: lwx * Date: 2019-03-11 * Time: 22:00 */ public class Test { public static void main(String[] args) { TestPaper testPaper = null; System.out.println("學生A答題"); testPaper = new StudentAPaper(); testPaper.answerQuestion(); System.out.println("----------------------------"); System.out.println("學生B答題"); testPaper = new StudentBPaper(); testPaper.answerQuestion(); } }
由於試卷是一樣的,所以完全可以抽象成父類,然后在子類中去填寫答案,這樣就是通過把不變的行為搬移到超類,去除子類中的重復代碼,這也是模板方法的特點。
使用過Servlet的人都清楚,需要繼承一個叫HttpServlet的抽象類。HttpServlet類提供了一個service()方法,這個方法調用七個do方法中的一個或幾個,完成對客戶端調用的響應。這些do方法需要由HttpServlet的具體子類提供,因此這是典型的模板方法模式。下面是service()方法的源代碼。
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); long lastModified; if (method.equals("GET")) { lastModified = this.getLastModified(req); if (lastModified == -1L) { this.doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if (ifModifiedSince < lastModified) { this.maybeSetLastModified(resp, lastModified); this.doGet(req, resp); } else { resp.setStatus(304); } } } else if (method.equals("HEAD")) { lastModified = this.getLastModified(req); this.maybeSetLastModified(resp, lastModified); this.doHead(req, resp); } else if (method.equals("POST")) { this.doPost(req, resp); } else if (method.equals("PUT")) { this.doPut(req, resp); } else if (method.equals("DELETE")) { this.doDelete(req, resp); } else if (method.equals("OPTIONS")) { this.doOptions(req, resp); } else if (method.equals("TRACE")) { this.doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[]{method}; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }
下面我們看下HttpServlet的結構圖,並寫一個簡單的Servlet例子。
package com.lwx.template_method.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * Created with IntelliJ IDEA. * Description: * User: lwx * Date: 2019-03-11 * Time: 22:05 */ @WebServlet(name = "myServlet", urlPatterns = "/myServlet") public class MyServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().write("hello, world"); } }
這里使用@WebServlet注解就不用在web.xml配置了,具體的內容可以看這篇博客 https://blog.csdn.net/mytt_10566/article/details/70173007,我們重寫了doGet和doPost,由於是簡單示例doPost我就不寫了,只在doGet返回 hello,world,最后我們用tomcat啟動工程。
模板方法的優點和缺點:
優點:
1.模板方法提供了一個很好的代碼復用平台。
2.實現了反向控制,通過一個父類調用其子類的操作,通過對子類的具體實現擴展不同的行為,實現了反向控制,符合 "開閉原則"。
缺點:
1.每一個不同的實現都需要一個子類來實現,導致類的個數增加,使得系統更加龐大。
最后附上demo的githup地址:https://github.com/yijinqincai/design_patterns