一、概念
1、定义:模版方法模式在一个方法中定义了算法流程中的每一个步骤(完成一件事的每一个步骤),把这些步骤的实现推迟到了子类。即模版是一套固定的算法,但是可以通过子类扩展固定算法的某些步骤。
2、类型:行为型
3、适用场景:
- 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。比如往冰箱里放东西这个操作,打开门和关上门的操作是不变的,具体放什么可以改变。
- 各子类中公共的行为被提取出来,放到一个公共父类中,从而避免代码的重复。
4、优缺点
- 优点
- 提高复用性和扩展性(相同的代码放在抽象的父类中)
- 提高扩展性(不同代码放入不同子类,来增加新的行为)
- 符合OCP
- 缺点
- 类数目增加
- 增加了系统实现的复杂度
- 模版方法通过继承实现,而继承自身的缺点是,如果父类添加新的抽象方法,子类都要改一遍
5、扩展 hook
方法,模版对子类更进一层的开放和扩展
6、相关设计模式
- 工厂方法是模版方法的特殊实现
- 和策略模式的区别
- 策略模式是使得算法间可以相互替换且不影响应用层的使用,模版是针对一个算法流程,将具体实现不一样的具体实现步骤交给子类去实现。模版不改变算法流程,而策略模式可以改变算法流程。
7、UML
二、Coding
以制作课程为例
模版类
//模版类,制作课程的步骤
public abstract class ACourse {
//ACourse中定义的模版是不想被修改的,所以方法被声明为final
protected final void makeCourse() {
this.makePPT(); //必须制作ppt
this.makeVideo(); //必须制作视频
if (needWriteArticle()) { //制作手记是可选的,这边需要使用hook,子类可以重写hook
this.writeArticle();
}
this.packageCourse();
}
//拆解小步骤
//所有的课程都要制作ppt(如果这个方法所有的子类都不需要重写,就声明为final)
final void makePPT() {
System.out.println("制作PPT");
}
final void makeVideo() {
System.out.println("制作视频");
}
//对于课程来说,编写手记是一个可选项,可写可不写
final void writeArticle() {
System.out.println("编写手记");
}
//hook方法,它不是final的,子类可以覆盖它
protected boolean needWriteArticle() {
return false;
}
//包装课程,对于不同的课程,包装的素材都是不一样的,交给子类来实现
abstract void packageCourse();
}
具体子类
//两个课程类
//设计模式课程类
public class DesignPannternCourse extends ACourse {
@Override
void packageCourse() {
System.out.println("提供课程的源代码");
}
}
//FE前端课程类
public class FECourse extends ACourse {
@Override
void packageCourse() {
System.out.println("提供前端代码");
System.out.println("提供图片素材");
}
//前端课程要编写手记,那就重写hook
@Override
protected boolean needWriteArticle() {
return true;
}
}
测试类
//Test
public class Test {
public static void main(String[] args) {
ACourse designPannternCourse = new DesignPannternCourse();
designPannternCourse.makeCourse();
System.out.println("-----------------------");
ACourse feCourse = new FECourse();
feCourse.makeCourse();
}
}
制作PPT
制作视频
提供课程的源代码
-----------------------
制作PPT
制作视频
编写手记
提供前端代码
提供图片素材
现在问题来了,前端课程中vue要写手记,而react不需要写手记,那怎么解决?
public class FECourse extends ACourse{
private boolean needWriteArticleFlag = false;
@Override
void packageCourse() {
System.out.println("提供前端课程的源代码");
System.out.println("提供图片素材");
}
//通过构造器和setter把它开放给应用层
public FECourse(boolean needWriteArticleFlag) {
this.needWriteArticleFlag = needWriteArticleFlag;
}
//这样是否需要写手记由应用层去决定
@Override
protected boolean needWriteArticle() {
return this.needWriteArticleFlag;
}
}
//Test
public class Test {
public static void main(String[] args) {
ACourse feCourse = new FECourse(true);
feCourse.makeCourse();
}
}
制作PPT
制作视频
编写手记
提供前端课程的源代码
提供图片素材
tip:模版中不可修改的方法用final
修饰,对于子类必须提供算法中的某个步骤的实现时,就使用抽象的方法,如果算法的这个部分是可选的,就可以用hook
,子类可以自己选择要不要覆盖hook
,一般可以利用hook
做一些决定。
三 、模版方法模式在JDK中的体现
ArrayList
的get
方法就是实现了父类AbstractList
的抽象get
方法,同样AbstractSet
、AbstractMap
也是同理,该开放的开放,该自己实现的自己实现。以及Comparable
接口的ComparTo
方法就是使用了模版方法模式,让我们可以自定义排序的规则等等。还有Servlet
中的doGet
与doPost
方法。