模板设计模式:
在生活中比如出去吃饭,①先下订单–》②吃饭–》③吃水果–》④结账,在整个过程中流程是不变的,但是第二个步骤可能因为人的不同国家或者名族吃饭方式不同(所以饭店不可能规定出吃饭的方式,客户自己选择吃饭的方式),第三个步骤可以客户可以自己选择要吃的水果,如果不选择那么商家就默认给你香蕉,第四个步骤由于商家没跟上互联网的时代,所以只能支持现金支付(这时候商家要求客户必须使用现金支付的)
package com.sxt;
class Chinese{
/**
* 这是必须的
*/
public void placeOrder() {
System.out.println("下订单");
}
/**
* 商家不能规定客户的吃饭方式,必须客户自己选择
*/
public void haveMeal() {
System.out.println("使用筷子吃饭");
}
/**
* 客户也可以自己选择要吃什么水果,如果不选也可以,但是会给默认的水果
*/
public void doHaveFruit() { //这种方法在模板设计 模式中叫做钩子方法
System.out.println("吃苹果");
}
/**
* 商家强制要求必须使用现金
*/
public void acount() {
System.out.println("只能现金结账");
}
}
//西方人
class European {
/**
* 这是必须的
*/
public void placeOrder() {
System.out.println("下订单");
}
/**
* 商家不能规定客户的吃饭方式,必须客户自己选择
*/
public void haveMeal() {
System.out.println("使用刀叉吃饭");
}
/**
* 客户也可以自己选择要吃什么水果,如果不选也可以,但是会给默认的水果
*/
public void doHaveFruit() { //这种方法在模板设计 模式中叫做钩子方法
System.out.println("吃苹果");
}
/**
* 商家强制要求必须使用现金
*/
public void acount() {
System.out.println("只能现金结账");
}
}
//非洲人
class Africa{
/**
* 这是必须的
*/
public void placeOrder() {
System.out.println("下订单");
}
/**
* 商家不能规定客户的吃饭方式,必须客户自己选择
*/
public void haveMeal() {
System.out.println("使用手抓");
}
/**
* 客户也可以自己选择要吃什么水果,如果不选也可以,但是会给默认的水果
*/
public void doHaveFruit() { //这种方法在模板设计 模式中叫做钩子方法
System.out.println("吃橘子");
}
/**
* 商家强制要求必须使用现金
*/
public void acount() {
System.out.println("只能现金结账");
}
}
public class Test{
public static void main(String[] args) {
//中国人去吃饭
Chinese chinese=new Chinese();
chinese.placeOrder();
chinese.haveMeal();
chinese.doHaveFruit();
chinese.acount();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//西方人人去吃饭
European european=new European();
european.placeOrder();
european.haveMeal();
european.doHaveFruit();
european.acount();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//非洲人去吃饭
Africa africa=new Africa();
africa.placeOrder();
africa.haveMeal();
africa.doHaveFruit();
africa.acount();
}
}
以上的三个类在使用的时候调用形式都是重复的, 此时为了解决这样的问题提出了模板设计模式, 定义出一个抽象父类,在该父类中组织子类方法的调用,在父类中定义出一个方法来实现子类方法调用的组织,这个方法叫做模板方法,该类叫做模板类,在模板类中一般会有如下特征的方法:
•模板方法:组织子类方法的调用,定义出了一个骨架
=•抽象方法:要求子类覆写的方法(比如说吃饭的方法就是要求子类自己选择方式)=
•final修饰的方法:该方法不让被子类覆写(结账的时候只能是现金支付)
•钩子方法:在父类中默认实现了,子类可以选择性的覆写,这样的方法的名称一般以do开头(吃水果的时候可以自己选择,也可以不选择,因为商家会按照默认方式给出水果)。
package com.sxt;
public class TestDemo04 {
public static void main(String[] args) {
Chinese c = new Chinese();
c.enjoy();
System.out.println("====================================");
Europe e = new Europe();
e.enjoy();
System.out.println("====================================");
Africa a = new Africa();
a.enjoy();
}
}
abstract class TempletPattern {
public final void placeOrder() {
System.out.println("下订单");
}
public abstract void haveMeal();
public void doHaveFruit() { //钩子方法,子类可以选择复写
System.out.println("吃苹果");
}
public final void acount() {
System.out.println("只支持现金支付");
}
public void enjoy() {
this.placeOrder();
this.haveMeal();
this.doHaveFruit();
this.acount();
}
}
class Chinese extends TempletPattern {
@Override
public void haveMeal() {
System.out.println("使用筷子吃饭");
}
}
class Europe extends TempletPattern {
@Override
public void haveMeal() {
System.out.println("使用刀叉吃饭");
}
@Override
public void doHaveFruit() {
System.out.println("吃草莓");
}
}
class Africa extends TempletPattern {
@Override
public void haveMeal() {
System.out.println("手抓");
}
@Override
public void doHaveFruit() {
System.out.println("吃香蕉");
}
}
此时就是使用模板设计模式定义出了代码的骨架,去出累赘的调用代码。
总结:
这里涉及到两个角色:
抽象模板(Abstract Template)角色有如下责任:
■ 定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作
■ 定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。
具体模板(Concrete Template)角色又如下责任:
■ 实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
模板方法的使用场景(了解)
使用过Servlet的人都清楚,除了要在web.xml做相应的配置外,还需继承一个叫HttpServlet的抽象类。HttpServlet类提供了一个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) {
// 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方法。再有这个模板方法中判断调用具体的doXXXX方法。