模板方法模式 (Template Method Pattern)

定义

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在一个方法中定义一个算法的骨架,将一些步骤的实现延迟到子类。通过这种方式,模板方法允许子类在不改变算法结构的情况下,重新定义算法的某些特定步骤。

模板方法模式通常包含以下两个角色:

  • 抽象类(Abstract Class):定义模板方法,以及算法的骨架。它可以包含具体方法、抽象方法和钩子(hook)方法。
  • 具体子类(Concrete Class):实现抽象类中定义的抽象方法和钩子方法,从而完成算法中特定子部分的实现。
解决的问题
  • 重复代码:在多个类中出现重复的代码是常见的问题,尤其是当这些类执行相似的任务时。模板方法模式通过将这些公共代码移到一个共同的父类中来减少重复。
  • 算法骨架的一致性:在某些情况下,你可能希望确保一个算法的基本结构保持一致,同时允许算法的某些步骤在不同的情境下有所不同。模板方法模式允许在保持算法结构不变的同时,让子类提供这些步骤的具体实现。
  • 扩展性:在不改变算法结构的情况下扩展功能。模板方法模式使得可以在子类中实现或重写某些步骤,而不需要更改基本算法的结构。
  • 反向控制结构(Inversion of Control):模板方法模式是一种反向控制结构,因为在这种模式中,高层模块(抽象类)控制算法的流程,底层模块(具体实现)则提供某些步骤的具体实现。这有助于解耦算法的定义和实现。
  • 提供算法的框架:当你想提供一个算法的框架让其他开发者使用时,模板方法模式是一个不错的选择。开发者可以根据自己的需求覆写预定义的步骤,而无需更改算法的核心流程。
使用场景
  • 算法步骤的共性和差异性分离
    • 当多个类有共同的行为,且这些行为中的步骤大体上相似,但每个步骤的具体实现不同时,模板方法模式非常有用。它允许你将共同的操作抽象到一个共有的父类中,而将各个步骤的具体实现留给子类。
  • 一次性实现算法的不变部分
    • 在需要实现一个算法,并且算法的大体框架是稳定的,但某些关键步骤可能会发生变化的情况下,使用模板方法模式可以固定算法的框架,并将变化的部分留给子类来实现。
  • 扩展类的功能
    • 当需要扩展类功能而不改变其主体结构时,模板方法模式提供了一种机制,允许在不改变类的主体结构的前提下对其进行扩展。
  • 避免代码重复
    • 在多个类中执行相似的操作时,模板方法模式可以帮助避免重复代码。通过在父类中编写通用代码,可以减少重复,并使维护变得更容易。
  • 控制子类扩展
    • 当你需要控制子类的扩展点时,模板方法模式是一个很好的选择。父类可以定义哪些操作是可变的,哪些是固定的,从而提供了一种方式来防止子类改变算法的结构。
示例代码
// 抽象类定义模板方法和抽象步骤
abstract class DataParser {
    // 模板方法
    public final void parseData() {
        readData();
        processData();
        writeData();
    }

    // 具体方法
    public void readData() {
        System.out.println("Reading data...");
    }

    // 抽象方法
    abstract protected void processData();

    // 具体方法
    public void writeData() {
        System.out.println("Writing data...");
    }
}

// 具体实现类 - XML解析
class XMLDataParser extends DataParser {
    @Override
    protected void processData() {
        System.out.println("Processing XML data");
    }
}

// 具体实现类 - JSON解析
class JSONDataParser extends DataParser {
    @Override
    protected void processData() {
        System.out.println("Processing JSON data");
    }
}

// 客户端代码
public class TemplateMethodDemo {
    public static void main(String[] args) {
        DataParser xmlParser = new XMLDataParser();
        xmlParser.parseData();

        System.out.println();

        DataParser jsonParser = new JSONDataParser();
        jsonParser.parseData();
    }
}
主要符合的设计原则
  • 开闭原则(Open-Closed Principle)
    • 模板方法模式允许算法的结构保持不变(“关闭”),同时可以通过继承来扩展部分步骤的实现(“开放”)。这意味着可以在不修改现有代码的情况下,通过添加新的子类来扩展功能。
  • 好莱坞原则(Hollywood Principle)
    • 这个原则通常被表述为“不要给我们打电话,我们会给你打电话”,指的是高层模块对低层模块的调用,而不是相反。模板方法模式中,抽象类定义了高层的算法结构,它会在适当的时候调用子类的方法,而子类通常不会直接调用抽象类的方法。
  • 单一职责原则(Single Responsibility Principle)
    • 模板方法模式将算法的框架(骨架)和具体步骤的实现分离。抽象类负责定义算法的结构和管理整个算法的流程,而各个步骤的具体实现则由子类负责,符合单一职责原则。
  • 里氏替换原则(Liskov Substitution Principle)
    • 在模板方法模式中,子类必须能够替换其基类。这是因为算法的框架是在基类中定义的,而基类可以在运行时被任何合适的子类实例替换,而不影响算法的整体结构和最终结果。
在JDK中的应用
  • Java I/O 类
    • java.io 包中,许多流类(如 InputStream, OutputStream, Reader, 和 Writer)都使用了模板方法模式。这些类中的某些方法定义了操作的基本结构,而具体的读写操作则由子类(如 FileInputStream, FileOutputStream, StringReader, 和 StringWriter)实现。
    • 例如,InputStream 中的 read() 方法是抽象的,定义了读取数据的框架,而具体的读取逻辑则由子类实现。
  • Java Collections Framework
    • java.util 包中,AbstractList, AbstractSetAbstractMap 等抽象类为集合类提供了模板方法。这些类实现了 CollectionMap 接口的大部分方法,而将一些核心方法留给子类实现。
    • 例如,AbstractList 提供了对 List 接口的大部分实现,但是具体的 get(int index), size(), set(int index, E element) 等方法则需要由子类提供。
  • Java Swing 库
    • 在Java Swing库中,许多GUI组件的绘制过程采用了模板方法模式。例如,paintComponent(Graphics g) 方法是一个模板方法,它定义了绘制组件的基本流程,而具体的绘制逻辑则由子类(如不同的按钮、面板等)实现。
  • Java Servlet API
    • javax.servlet.http.HttpServlet 类中,doGet, doPost, doPut, doDelete 等方法实现了HTTP请求的处理框架,而具体的处理逻辑则由开发者在子类中实现。
在Spring中的应用
  • JdbcTemplate
    • JdbcTemplate 是Spring提供的用于简化JDBC操作的一个经典例子。它定义了执行数据库操作的基本流程(如建立连接、准备语句、执行查询、处理结果集),而将结果集的解析和数据的处理逻辑留给开发者通过回调来实现。这就是模板方法模式的典型应用。
  • HibernateTemplate 和 HibernateDaoSupport
    • 类似于 JdbcTemplateHibernateTemplateHibernateDaoSupport 提供了一个框架来简化Hibernate操作。它们处理了开启会话、事务的管理等,而具体的Hibernate调用则是在回调中实现的。
  • Spring Web MVC的Controller
    • 在Spring MVC中,AbstractController 提供了处理HTTP请求的模板方法。它处理请求的流程(如请求的接收和响应的发送),而具体的请求处理逻辑则由开发者在扩展的类中实现。
  • Spring Security的认证和授权
    • 在Spring Security中,例如 AbstractAuthenticationProcessingFilter 提供了身份验证的模板方法。这个类处理了认证流程的大部分步骤,而具体的认证逻辑则是由开发者在子类中自定义的。