定义 定义了一个算法的骨架,并允许子类为一个或多个步骤提供实现。模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。
类型 行为型
使用场景 1 2 ◆ 各子类中公共的行为被提取出来并集中到一个公共父类中,从而避免代码重复 ◆ 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
优点 1 2 3 ◆提高复用性(将相同代码部分放到抽象父类中) ◆提高扩展性 ◆符合开闭原则
缺点 1 2 3 ◆继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍 ◆类数目增加 ◆增加了系统实现的复杂度
模版方法扩展
模版方法中有一个定义:钩子方法。它提供了缺省的行为,子类可以在必要时进行扩展,利用它和父类交互。即构造方法是这个模版对子类更进一步的开放以及扩展。
相关的设计模式 模版方法模式和工厂方法模式
工厂方法是模版方法的一种特殊实现
模版方法模式和策略模式
模版方法模式不改变算法流程,策略模式可以改变算法流程,并且策略方法之间是可以相互替换的。策略模式的目的是使不同的算法可以相互替换,并且不影响应用层客户端的使用。模板方法模式是针对定义一个算法的流程,将一些不太一样的具体步骤交给子类去实现。
简单需求 某机构要制作一门课程,制作这个课程需要一定的步骤,由于课程的种类不同可能某个步骤不同,但是整体步骤都是一致的。
模版方法演练
抽象课程类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package com.design.pattern.template;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic abstract class Course { protected final void makeCourse () { makePPT(); makeVideo(); if (needMakeArticle()){ makeArticle(); } packageCourse(); } final void makePPT () { log.info("制作ppt" ); } final void makeVideo () { log.info("制作视频" ); } final void makeArticle () { log.info("编写手记" ); } protected boolean needMakeArticle () { return false ; } abstract void packageCourse () ; }
课程类1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.design.pattern.template;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class FECourse extends Course { @Override void packageCourse () { log.info("提供前端课程的源代码和图片素材" ); } }
课程类2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.design.pattern.template;import lombok.extern.slf4j.Slf4j;@Slf 4jpublic class JvmCource extends Course { private boolean flag = Boolean.FALSE; public JvmCource (boolean flag) { this .flag = flag; } @Override void packageCourse () { log.info("Jvm课程提供调优工具软件包" ); } @Override protected boolean needMakeArticle () { return flag; } }
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.design.pattern.template;import org.junit.Test;public class Client { @Test public void test () { Course jvmCourse = new JvmCource(Boolean.TRUE); jvmCourse.makeCourse(); Course feCourse = new FECourse(); feCourse.makeCourse(); } }
模版方法模式源码解析 AbstractList(父)-ArrayList(子) AbstractList
1 2 3 4 5 6 7 public abstract class AbstractList <E > extends AbstractCollection <E > implements List <E > {abstract public E get (int index) ;}
ArrayList
1 2 3 4 5 6 7 8 9 10 public class ArrayList <E > extends AbstractList <E > implements List <E >, RandomAccess , Cloneable , java .io .Serializable { public E get (int index) { rangeCheck(index); return elementData(index); } }
同理:AbstractSet、AbstractMap同样采用了模版方法模式
HttpServlet 1 我们一般继承HttpServlet,然后重写doGet或者doPost等doXxx方法,HttpServlet中定义了一套模版,只要覆写这些方法即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 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 ) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < (lastModified / 1000 * 1000 )) { 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 { 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); } }