JAVA-模板設計模式
模板方法模式是類的行為模式。準備一個抽象類,將部分邏輯以具體方法以及具體建構函式的形式實現,然後宣告一些抽象方法來迫使子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。這就是模板方法模式的用意。(引用自閻巨集博士的《JAVA與模式-模板方法》。
模板方法模式是所有模式中最為常見的幾個模式之一,是基於繼承的程式碼複用的基本技術。
模板方法模式需要開發抽象類和具體子類的設計師之間的協作。一個設計師負責給出一個演算法的輪廓和骨架,另一些設計師則負責給出這個演算法的各個邏輯步驟。代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法彙總起來的方法叫做模板方法(template method),這個設計模式的名字就是從此而來。
模板方法所代表的行為稱為頂級行為,其邏輯稱為頂級邏輯。模板方法模式的靜態結構圖如下所示:
這裡涉及到兩個角色:
抽象模板(Abstract Template)角色有如下責任:
■ 定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟。
■ 定義並實現了一個模板方法。這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能呼叫一些具體方法。
具體模板(Concrete Template)角色又如下責任:
■ 實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。
■ 每一個抽象模板角色都可以有任意多個具體模板角色與之對應,而每一個具體模板角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。
===以上引用其他人的部落格===
示例程式碼:
定義頂層模板抽象方法:
定義具體的子類實現:package test.edu.havemeals; public abstract class AbsHaveMeals { public final void HaveMealsFlow() { food(); tableWare(); howToHaveMeals(); } public abstract void food(); public abstract void tableWare(); public void howToHaveMeals() { System.out.println("用嘴直接嚼食物,並下嚥"); } }
package edu.test.whotoeat;
import test.edu.havemeals.AbsHaveMeals;
public class Cat extends AbsHaveMeals{
@Override
public void food() {
// TODO Auto-generated method stub
System.out.println("貓咪吃貓糧");
}
@Override
public void tableWare() {
// TODO Auto-generated method stub
System.out.println("餐具:貓糧專用碗");
}
}
package edu.test.whotoeat;
import test.edu.havemeals.AbsHaveMeals;
public class Chinese extends AbsHaveMeals{
@Override
public void food() {
// TODO Auto-generated method stub
System.out.println("中國人吃麵條");
}
@Override
public void tableWare() {
// TODO Auto-generated method stub
System.out.println("餐具:筷子,碗");
}
}
package edu.test.whotoeat;
import test.edu.havemeals.AbsHaveMeals;
public class Patient extends AbsHaveMeals {
@Override
public void food() {
// TODO Auto-generated method stub
System.out.println("病人專用營養液");
}
@Override
public void tableWare() {
// TODO Auto-generated method stub
System.out.println("餐具:營養液容器,導管");
}
@Override
public void howToHaveMeals() {
System.out.println("用導管直接為病人身體輸送營養"); //如何模板方法在子類中不適合使用,可以在子類重寫之
}
}
定義產生具體子類的工廠:
package test.edu.client;
import test.edu.havemeals.AbsHaveMeals;
public class HaveMealsFactory {
public static AbsHaveMeals getAbsHaveMeals(Class<? extends AbsHaveMeals> ah){
try {
return ah.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
客戶端類:
package test.edu.client;
import edu.test.whotoeat.Cat;
import edu.test.whotoeat.Chinese;
import edu.test.whotoeat.Patient;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
HaveMealsFactory.getAbsHaveMeals(Chinese.class).HaveMealsFlow();
HaveMealsFactory.getAbsHaveMeals(Cat.class).HaveMealsFlow();
HaveMealsFactory.getAbsHaveMeals(Patient.class).HaveMealsFlow();
}
}
執行結果:
中國人吃麵條
餐具:筷子,碗
用嘴直接嚼食物,並下嚥
貓咪吃貓糧
餐具:貓糧專用碗
用嘴直接嚼食物,並下嚥
病人專用營養液
餐具:營養液容器,導管
用導管直接為病人身體輸送營養
模板方法模式中的方法
模板方法中的方法可以分為兩大類:模板方法和基本方法。
模板方法
一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總演算法或一個總行為的方法。
一個抽象類可以有任意多個模板方法,而不限於一個。每一個模板方法都可以呼叫任意多個具體方法。
基本方法
基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)。
● 抽象方法:一個抽象方法由抽象類宣告,由具體子類實現。在Java語言裡抽象方法以abstract關鍵字標示。
● 具體方法:一個具體方法由抽象類宣告並實現,而子類並不實現或置換。
● 鉤子方法:一個鉤子方法由抽象類宣告並實現,而子類會加以擴充套件。通常抽象類給出的實現是一個空實現,作為方法的預設實現。
在上面的例子中,AbstractTemplate是一個抽象類,它帶有三個方法。其中abstractMethod()是一個抽象方法,它由抽象類宣告為抽象方法,並由子類實現;hookMethod()是一個鉤子方法,它由抽象類宣告並提供預設實現,並且由子類置換掉。concreteMethod()是一個具體方法,它由抽象類宣告並實現。
預設鉤子方法
一個鉤子方法常常由抽象類給出一個空實現作為此方法的預設實現。這種空的鉤子方法叫做“Do Nothing Hook”。顯然,這種預設鉤子方法在預設適配模式裡面已經見過了,一個預設適配模式講的是一個類為一個介面提供一個預設的空實現,從而使得預設適配類的子類不必像實現介面那樣必須給出所有方法的實現,因為通常一個具體類並不需要所有的方法。
模板方法模式在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使用例項網上可以搜到很多,這裡不贅述.