0.参考资料
1.概述
- 定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(即复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。——《设计模式》GoF
- 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),z在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
1.1动机
- 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
- 在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求.
1.2结构
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629202068871-4bed0b6c-5be3-4fd0-b1c1-b8487144798e.png#clientId=u9a695ff8-36c5-4&from=paste&height=194&id=u2d9de518&margin=%5Bobject%20Object%5D&name=image.png&originHeight=388&originWidth=581&originalType=binary&ratio=1&size=27651&status=done&style=none&taskId=u2868059f-2135-40b0-9d8b-72b97eadc5f&width=290.5)
- 说明:
- AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法operationr2,3,4
- ConcreteClass 实现抽象方法operationr2,3,4, 以完成算法中特点子类的步
骤
- 结构化和面对对象设计区别:
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629189221040-2b23ae7f-7214-46a1-af7a-4e8e5b80184c.png#clientId=u2ff7bb69-957a-4&from=paste&height=173&id=u81210cd2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=345&originWidth=645&originalType=binary&ratio=1&size=43086&status=done&style=none&taskId=u2547e74a-d508-41c8-89ca-bb42b774953&width=322.5)
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629189246479-f441654a-6994-429e-819d-764cb2c6d869.png#clientId=u2ff7bb69-957a-4&from=paste&height=167&id=u1e1ddad8&margin=%5Bobject%20Object%5D&name=image.png&originHeight=333&originWidth=647&originalType=binary&ratio=1&size=42640&status=done&style=none&taskId=u84e3ffd4-bce5-4e3b-8f6d-d2d6d1db02d&width=323.5)
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629189270572-c5e66f2f-34a2-4224-83df-18b652b44ee5.png#clientId=u2ff7bb69-957a-4&from=paste&height=168&id=u68dde0e1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=335&originWidth=633&originalType=binary&ratio=1&size=32490&status=done&style=none&taskId=u11068217-63fa-4acc-8531-23307d41d92&width=316.5)
2.要点总结
宏观架构
1. Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
1. 除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。(即 "依赖倒置原则": 实现细节依赖于稳定的抽象)
1. 在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为protected方法。
微观代码
1. 基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
1. 实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。
1. 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。
1. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大
1. 一般模板方法都加上final关键字, 防止子类重写模板方法.
1. 模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤 ,这一系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模式来处理
3.案例
略…
4.使用模式
方案
- 一个简单的模板方法模式
钩子方法
- 模板方法模式的父类中,可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”。
类图
- ![(TL7HHN91M~3E9198F1{IIU.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629202386464-b9b7d704-f4cf-4850-b894-f508afbf3fe7.png#clientId=u9a695ff8-36c5-4&from=paste&height=347&id=ucab89375&margin=%5Bobject%20Object%5D&name=%28TL7HHN91M~3E9198F1%7BIIU.png&originHeight=694&originWidth=673&originalType=binary&ratio=1&size=45379&status=done&style=none&taskId=ua6643412-a39c-4a71-884f-394ade42038&width=336.5)
代码
- AbstractClass
public abstract class AbstractClass {
// 模板方法, final修饰, 防止被重写
public final void templateMethod(){
System.out.println("第1步-----------------");
concreteStep1();
System.out.println("第2步-----------------");
concreteStep3();
System.out.println("第3步-----------------");
// 使用(抽象)钩子方法判断是否进行 step5
if (step2()){
System.out.println("第4步-----------------准备循环");
// 调用抽象方法step4, 判断实际的step5进行次数
for (int i = step4(), count = 0; i > 0; i--){
System.out.println("第4步-----------------" + ++count);
concreteStep5();
}
}
}
public void concreteStep1(){
System.out.println("step1:启动");
}
public void concreteStep3(){
System.out.println("step3: 启动成功");
}
public void concreteStep5(){
System.out.println("step5: 扩展");
}
/**
* @description: 钩子方法, 默认不做任何事
*/
protected boolean step2(){
return false;
};
protected abstract int step4();
}
- ConcreteClass
public class ConcreteClass extends AbstractClass{
@Override
protected boolean step2() {
boolean flag = true;
System.out.println("step2: 判断中--" + flag);
return flag;
}
@Override
protected int step4() {
int count = 3;
System.out.println("step4: 准备循环" + count);
return count;
}
}
- 测试客户端及结果
public class Client {
public static void main(String[] args) {
AbstractClass concreteClass = new ConcreteClass();
concreteClass.templateMethod();
}
}
第1步-----------------
step1:启动
第2步-----------------
step3: 启动成功
第3步-----------------
step2: 判断中--true
第4步-----------------准备循环
step4: 准备循环3
第4步-----------------1
step5: 扩展
第4步-----------------2
step5: 扩展
第4步-----------------3
step5: 扩展
5.经典使用
5.1Spring中IOC容器的初始化
5.1源码分析
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629203260711-cb4dd773-f667-4e96-ae5f-f406f102b555.png#clientId=u9a695ff8-36c5-4&from=paste&height=292&id=ufa868354&margin=%5Bobject%20Object%5D&name=image.png&originHeight=584&originWidth=1032&originalType=binary&ratio=1&size=1010647&status=done&style=none&taskId=u1c5d56f8-f909-4131-bdfe-cde72b0e1a0&width=516)
5.2类图
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12524106/1629203303733-ba5e4322-7ea8-4c35-b683-b26e43178c0b.png#clientId=u9a695ff8-36c5-4&from=paste&height=292&id=u8bf94a0c&margin=%5Bobject%20Object%5D&name=image.png&originHeight=584&originWidth=1110&originalType=binary&ratio=1&size=376669&status=done&style=none&taskId=u5ae6a4b4-2cfc-4c08-a542-668bbb99bb0&width=555)
5.2JavaWeb中HttpServlet抽象类的service(…)
说明:
- 方法功能: 把处理具体HTTP请求的流程构建好, 但是具体业务的具体请求是不确定的(即doGet/doPost等), 所以封装成service(...), 再获取请求头信息确定具体执行的HTTP请求类型. 最后该请求类型的具体实现则由其具体子类实现. HttpServlet中的各种doXxx(...)均是异常方法, 根据具体业务具体实现
- HttpServlet中, service(...) 即是模板方法. 其中的各种将抛出异常的 doXxx(...)均是钩子方法
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;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
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);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}