一:定义:
Template Method:Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
二:引入
一个咖啡店,卖咖啡和茶。
制作咖啡的步骤是:
- 把水煮开。
- 放入咖啡。
- 把煮好的咖啡倒进杯子。
- 加糖或奶。
制作茶的步骤是:
- 把水煮开。
- 放入茶叶。
- 把茶倒进茶杯。
- 加柠檬或其他调料。
public
class
Coffee
...
{
public void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
public void brew()
...{
System.out.println("brew coffee");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
public void addCondiments()
...{
System.out.println("add sugar");
}
}
public class Tea ... {
public void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
public void brew()
...{
System.out.println("brew tea");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
public void addCondiments()
...{
System.out.println("add lemon");
}
}
public void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
public void brew()
...{
System.out.println("brew coffee");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
public void addCondiments()
...{
System.out.println("add sugar");
}
}
public class Tea ... {
public void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
public void brew()
...{
System.out.println("brew tea");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
public void addCondiments()
...{
System.out.println("add lemon");
}
}
我们发现,制作两种饮料的方法有些地方是类似的,我们可以对这两个类进行抽象。
public
abstract
class
CaffeineBeverage
...
{
public final void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
abstract void brew();
abstract void addCondiments();
}
public class Coffee extends CaffeineBeverage ... {
public void brew()
...{
System.out.println("brew coffee");
}
public void addCondiments()
...{
System.out.println("add sugar");
}
}
public class Client ... {
public static void main(String[] args) ...{
CaffeineBeverage beverage=new Coffee();
beverage.prepareRecipe();
}
}
public final void prepareRecipe()
...{
boilWater();
brew();
pourInCup();
addCondiments();
}
void boilWater()
...{
System.out.println("boil water");
}
void pourInCup()
...{
System.out.println("pour in cup");
}
abstract void brew();
abstract void addCondiments();
}
public class Coffee extends CaffeineBeverage ... {
public void brew()
...{
System.out.println("brew coffee");
}
public void addCondiments()
...{
System.out.println("add sugar");
}
}
public class Client ... {
public static void main(String[] args) ...{
CaffeineBeverage beverage=new Coffee();
beverage.prepareRecipe();
}
}
父类提供了一个按步骤执行方法的模板,公共部分已经在父类实现,其它由具体子类自己实现。
三:结构
四:实际应用
- javx.servlet.http.HttpServlet
HttpServlet代码,doGet等方法由子类实现。
public
abstract
class
HttpServlet
extends GenericServlet
implements Serializable
... {
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
...{
if(request.getMethod().equals("GET") )
...{
if(testConditional(request,response))
doGet(request,response);
}
else if (request.getMethod().equals("HEAD") )
...{
if(testConditional(request,response))
doHead(request,response);
}
else if (request.getMethod().equals("POST") )
...{
doPost(request,response);
}
else if (request.getMethod().equals("DELETE") )
...{
doDelete(request,response);
}
else if (request.getMethod().equals("OPTIONS") )
...{
doOptions(request,response);
}
else if (request.getMethod().equals("PUT") )
...{
doPut(request,response);
}
else if (request.getMethod().equals("TRACE") )
...{
doTrace(request,response);
}
else
...{
response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Method ""
+request.getMethod()+"" is not supported by this servlet");
}
}
protected void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
...{
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Method "GET" is not supported by this servlet");
}
}
extends GenericServlet
implements Serializable
... {
protected void service(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
...{
if(request.getMethod().equals("GET") )
...{
if(testConditional(request,response))
doGet(request,response);
}
else if (request.getMethod().equals("HEAD") )
...{
if(testConditional(request,response))
doHead(request,response);
}
else if (request.getMethod().equals("POST") )
...{
doPost(request,response);
}
else if (request.getMethod().equals("DELETE") )
...{
doDelete(request,response);
}
else if (request.getMethod().equals("OPTIONS") )
...{
doOptions(request,response);
}
else if (request.getMethod().equals("PUT") )
...{
doPut(request,response);
}
else if (request.getMethod().equals("TRACE") )
...{
doTrace(request,response);
}
else
...{
response.sendError(HttpServletResponse.SC_BAD_REQUEST,"Method ""
+request.getMethod()+"" is not supported by this servlet");
}
}
protected void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
...{
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Method "GET" is not supported by this servlet");
}
}
一个例子:
public
class
HelloServlet
extends
HttpServlet
...
{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
...{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Hello, World");
out.close();
}
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
...{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Hello, World");
out.close();
}
}
五:适用情形
The Template Method pattern should be used
- to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary.
- when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is a good example of "refactoring to generalize" as described by Opdyke and Johnson [OJ93]. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations.
- to control subclasses extensions. You can define a template method that calls "hook" operations (see Consequences) at specific points, thereby permitting extensions only at those points.
参考文献:
2:Eric Freeman & Elisabeth Freeman,《Head First Design Pattern》,O'REILLY
3:GOF,《designpatterns-elements.of.reuseable.object-oriented.software》