简介
模板方法模式是一种行为型设计模式,它定义一个操作中的算法骨架,将一些步骤推迟到子类中。模板方法模式使得子类可以不改变一个算法的结构,即可重定义该算法的某些特定步骤。
在模板方法模式中,抽象类中定义了一系列基本操作,这些操作是具体的也可以是抽象的,每一个基本操作对应算法的一个步骤。在子类中可以重定义或实现这些步骤,同时抽象类实现了一个模板方法,定义一个算法的框架。
模板方法模式通过封装不变部分,扩展可变部分,将特定步骤的具体实现与操作流程分离开来,实现了代码的复用和扩展,提高了代码质量和可维护性。同时,由于模板方法模式将具体实现由子类来完成,因此可以方便地扩展新的功能或变更实现方式,同时不影响模板方法本身。
然而,模板方法模式也存在一些缺点。由于每个算法都需要一个抽象类和具体子类来实现,因此在操作流程比较多时可能导致类的数量急剧增加,从而导致代码的复杂性提高。同时,由于模板方法和子类紧密相关,如果该模板方法需要修改,可能会涉及到多个子类的修改。
结构
模板方法(Template Method)模式包含以下主要角色:
-
抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。
-
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
-
基本方法:是实现算法各个步骤的方法,是模板方法的组成部分。基本方法又可以分为三种:
-
抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。
-
具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。
-
钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。
-
-
-
具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。
案例实现
【例】炒菜
炒菜的步骤是固定的,分为倒油、热油、倒蔬菜、倒调料品、翻炒等步骤。现通过模板方法模式来用代码模拟。类图如下:
代码如下:
public abstract class AbstractClass {
public final void cookProcess() {
//第一步:倒油
this.pourOil();
//第二步:热油
this.heatOil();
//第三步:倒蔬菜
this.pourVegetable();
//第四步:倒调味料
this.pourSauce();
//第五步:翻炒
this.fry();
}
public void pourOil() {
System.out.println("倒油");
}
//第二步:热油是一样的,所以直接实现
public void heatOil() {
System.out.println("热油");
}
//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)
public abstract void pourVegetable();
//第四步:倒调味料是不一样
public abstract void pourSauce();
//第五步:翻炒是一样的,所以直接实现
public void fry(){
System.out.println("炒啊炒啊炒到熟啊");
}
}
public class ConcreteClass_BaoCai extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是包菜");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是辣椒");
}
}
public class ConcreteClass_CaiXin extends AbstractClass {
@Override
public void pourVegetable() {
System.out.println("下锅的蔬菜是菜心");
}
@Override
public void pourSauce() {
System.out.println("下锅的酱料是蒜蓉");
}
}
public class Client {
public static void main(String[] args) {
//炒手撕包菜
ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
baoCai.cookProcess();
//炒蒜蓉菜心
ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
caiXin.cookProcess();
}
}
注意:为防止恶意操作,一般模板方法都加上 final 关键词。
优缺点
优点:
-
提高代码复用性
将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。
-
实现了反向控制
通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。
缺点:
- 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
- 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
源码中的应用
JDK中
-
java.io.InputStream:这是 Java 输入流的抽象类。它定义了一系列读取字节的方法,如
read()
和read(byte[] b, int off, int len)
。它的子类,如FileInputStream
和ByteArrayInputStream
等,实现了这些抽象方法以提供不同的输入来源。https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/io/InputStream.java
-
java.util.AbstractList:这是 Java 集合框架中 List 接口的抽象实现。它实现了 List 接口中的大部分方法,例如
get(int index)
和size()
,而add(int index, E element)
和remove(int index)
等修改列表的方法则被定义为抽象,留给具体的子类实现。https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/AbstractList.java
-
javax.servlet.http.HttpServlet:这是 Java Servlet API 中的一个关键类,用于处理 HTTP 请求。
HttpServlet
定义了doGet()
、doPost()
等用于处理不同 HTTP 方法的抽象方法,开发人员可以通过扩展HttpServlet
并覆盖这些方法来编写自定义的 HTTP 请求处理逻辑。
Spring中
- JdbcTemplate:
JdbcTemplate
是 Spring JDBC 模块中的一个核心类,它简化了 JDBC 编程。JdbcTemplate
使用了模板方法模式,定义了一组模板方法,如execute()
、query()
、update()
等,这些方法定义了 JDBC 操作的通用流程,但将实际的 SQL 查询、参数设置等操作留给了回调接口或者 Lambda 表达式,使得用户可以根据需要进行定制。 - RestTemplate:在 Spring Web 模块中,
RestTemplate
是一个用于访问 RESTful 服务的客户端类。它也使用了模板方法模式,定义了一组模板方法,如getForObject()
、postForObject()
等,这些方法封装了 HTTP 请求的通用逻辑,但实际的 HTTP 请求发送和响应处理由具体的 HTTP 消息转换器等组件来实现。 - AbstractController:在 Spring MVC 中,
AbstractController
是一个抽象控制器类,它定义了一些处理请求的模板方法,如handleRequestInternal()
。开发者可以通过扩展AbstractController
并实现这些模板方法来创建自定义的控制器,从而实现特定的请求处理逻辑。