模板方法模式及其在Java中的典型应用示例
1、什么是模板方法模式
模板方法模式定义:定义一个操作的算法骨架,而将一些步骤延迟到子类中。TemplateMethod使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。在模板方法模式中,一个抽象类公开定义了执行它的方法的方式/模板,它的子类可以按需重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。模板方法模式的类图示例如下:
2、模板方法模式的特性
(1)意图:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
(2)主要解决:
一些方法通用,却在每一个子类都重新写了这一方法。
(3)何时使用:
有一些通用的方法。
(4)如何解决:
将这些通用算法抽象出来。
(5)关键代码:
在抽象类实现,其他步骤在子类实现。
3、模板方法模式的优缺点及其应用场景
(1)优点:
1)封装不变部分,扩展可变部分;
2)提取公共代码,便于维护;
3)行为由父类控制,子类实现。
(2)缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
(3)使用场景:
1)有多个子类共有的方法,且逻辑相同。
2)重要的、复杂的方法,可以考虑作为模板方法。
(4)注意事项:
为防止恶意操作,一般模板方法都加上 final 关键词。
4、模板方法模式应用示例
(1)场景假设:
假设我们使用数据库,在操作数据库时,对数据库的连接初始化,预处理、用户执行相应的sql语句、关闭连接等步骤都是统一需要执行的,而在这之中,只要用户执行相应的sql语句这个操作是不同的,其他行为都是相同的,我们就可以将其交给系统模板来统一完成,每次子类只需要需要对应的用户操作部分即可。
(2)示例代码:
定义一个抽象模板类:其中的算法实现流程都是固定的,而只有其中的一部分操作(templateMethod()部分)是根据不同的子类可以有不同的实现:
abstract class AbstractClass{
public void operation(){
System.out.println("pre...");
System.out.println("step1...");
System.out.println("step2...");
templateMethod();
//...
System.out.println("end....");
}
abstract protected void templateMethod();
}
定义一个子类示例:后续我们需要增加不同的使用实例时,只需要让该类继承AbstractClass类并重写其中的模板方法,在其中进行自己的实现即可:
class SubClass extends AbstractClass{
@Override
protected void templateMethod() {
System.out.println("SubClass executed....");
}
}
测试示例:
public class TemplateMethodTest {
public static void main(String[] args) {
AbstractClass abstractClass = new SubClass();
abstractClass.operation();
}
}
结果输出示例:
pre...
step1...
step2...
SubClass executed....
end....
5、模板方法模式在Java中的典型应用示例
(1)模板方法在Servlet API中的应用示例:javax.servlet.http.HttpServlet类,service()方法定义了一套算法骨架,具体的实现通过子类实现,doPost,doGet...来完成;该方法源码如下:
/**
* Receives standard HTTP requests from the public
* <code>service</code> method and dispatches
* them to the <code>do</code><i>Method</i> methods defined in
* this class. This method is an HTTP-specific version of the
* {@link javax.servlet.Servlet#service} method. There's no
* need to override this method.
*
* @param req the {@link HttpServletRequest} object that
* contains the request the client made of
* the servlet
*
* @param resp the {@link HttpServletResponse} object that
* contains the response the servlet returns
* to the client
*
* @exception IOException if an input or output error occurs
* while the servlet is handling the
* HTTP request
*
* @exception ServletException if the HTTP request
* cannot be handled
*
* @see javax.servlet.Servlet#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;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
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);
}
}
可以看到,该方法接收来自公共标准HTTP请求,并更加其请求的类型将其分发到do这一类中定义的方法的方法。
(2)模板方法在Spring 中的应用:org.springframework.web.servlet.mvc.AbstractController类,handleRequest()定义了算法的骨架,用户可以实现handleRequestInternal()方法来实现算法的实现。源码如下:
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (HttpMethod.OPTIONS.matches(request.getMethod())) {
response.setHeader("Allow", getAllowHeader());
return null;
}
// Delegate to WebContentGenerator for checking and preparing.
checkRequest(request);
prepareResponse(response);
// Execute handleRequestInternal in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return handleRequestInternal(request, response);
}
}
}
return handleRequestInternal(request, response);
}
/**
* Template method. Subclasses must implement this.
* The contract is the same as for {@code handleRequest}.
* @see #handleRequest
*/
protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception;
本文源代码:
https://github.com/JianfuYang/2020-yjf-review/tree/master/src/designpatterns/templatemethod
声明:本文部分内容整理来源于网络,仅做个人学习使用!侵删~
本文部分内容参考链接:
https://www.runoob.com/design-pattern/template-pattern.html