设计模式 行为型 模板方法模式(Template Method Pattern)与 常见技术框架应用 解析

模板方法模式(Template Method Pattern)是软件设计模式中的一种行为设计模式。它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下重定义算法的某些特定步骤。

假设我们在制作不同种类的咖啡,如拿铁、美式咖啡等。它们都有一些共同的步骤,比如研磨咖啡豆、冲泡咖啡,但每种咖啡又有一些独特的步骤,比如拿铁需要加入牛奶,美式咖啡不需要。模板方法模式就像是一个咖啡制作的模板,它定义了制作咖啡的基本流程(公共步骤),同时允许不同种类的咖啡(具体子类)去实现自己独特的步骤。

一、核心思想

模板方法模式的核心思想是将算法中不变的部分抽象出来,以便子类可以重用该部分,而将变化的部分留给子类来实现。这提高了代码的复用性和可扩展性。就像在上面的咖啡例子中,咖啡制作的整体流程(研磨 - 冲泡 - 可能的其他添加步骤)是固定的骨架,但是添加牛奶这个步骤可以在拿铁咖啡的子类中具体实现。

二、定义与结构

  • 定义:在抽象类中定义一个模板方法,这个模板方法定义了算法的骨架,它包含了一系列的方法调用,其中一些方法是抽象方法,需要由具体子类来实现。
  • 结构
    • 抽象类(Abstract Class):包含模板方法和抽象方法。模板方法定义了算法的框架,抽象方法是需要子类实现的具体步骤。
    • 具体子类(Concrete Subclass):实现抽象类中的抽象方法,完成算法中特定步骤的具体逻辑。

三、角色

  • 抽象模板(Abstract Template)
    • 它是抽象类,定义了模板方法,这个方法通常是一个final方法,以防止子类修改算法的整体结构。同时,它还定义了抽象方法,这些抽象方法是算法中的可变部分,留给子类去实现。例如,在一个文档生成系统中,抽象模板可以是一个抽象的文档生成类,它有一个generateDocument模板方法,里面包含了读取数据、格式化数据等步骤,其中格式化数据的具体方式可以是抽象方法。
  • 具体模板(Concrete Template)
    • 实现抽象类中的抽象方法,完成具体的业务逻辑。继续以文档生成系统为例,具体模板可以是生成HTML文档的类和生成PDF文档的类。它们都实现了抽象文档生成类中的抽象方法,如格式化数据的方法,但具体的实现方式不同,一个是按照HTML的格式要求,一个是按照PDF的格式要求。

四、实现步骤及代码示例

  • 首先定义抽象类:
java 复制代码
// 抽象的咖啡制作类
abstract class Coffee {
    // 模板方法,制作咖啡的流程
    public final void makeCoffee() {
        grindBeans();
        brewCoffee();
        addExtraIngredients();
    }
    // 研磨咖啡豆,具体实现留给子类
    protected abstract void grindBeans();
    // 冲泡咖啡,具体实现留给子类
    protected abstract void brewCoffee();
    // 添加额外的配料,具体实现留给子类
    protected abstract void addExtraIngredients();
}
  • 然后定义具体子类:
java 复制代码
// 美式咖啡类
class Americano extends Coffee {
    @Override
    protected void grindBeans() {
        System.out.println("研磨美式咖啡的咖啡豆");
    }
    @Override
    protected void brewCoffee() {
        System.out.println("用滴滤方式冲泡美式咖啡");
    }
    @Override
    protected void addExtraIngredients() {
        System.out.println("美式咖啡不需要额外添加配料");
    }
}
// 拿铁咖啡类
class CaffeLatte extends Coffee {
    @Override
    protected void grindBeans() {
        System.out.println("研磨拿铁咖啡的咖啡豆");
    }
    @Override
    protected void brewCoffee() {
        System.out.println("用蒸汽方式冲泡拿铁咖啡");
    }
    @Override
    protected void addExtraIngredients() {
        System.out.println("加入适量的牛奶制作拿铁咖啡");
    }
}
  • 最后使用:
java 复制代码
public class Main {
    public static void main(String[] args) {
        Coffee americano = new Americano();
        americano.makeCoffee();
        System.out.println("----------------");
        Coffee caffeLatte = new CaffeLatte();
        caffeLatte.makeCoffee();
    }
}

五、常见技术框架应用

1、模板方法模式在前端框架中的应用

模板方法模式(Template Method Pattern)在前端框架中的应用主要体现在定义一系列算法步骤的框架,允许子类在不改变算法结构的情况下,通过实现或重写某些特定步骤来定制算法的具体行为。以下是在前端框架中应用模板方法模式的几个具体例子:

React组件中的生命周期方法

在React中,组件的生命周期提供了一系列可以重写的方法,这些方法定义了一个组件实例从创建到销毁所经历的各个阶段。React框架本身定义了这些生命周期方法的框架(如componentDidMountcomponentDidUpdatecomponentWillUnmount等),而开发者可以在自己的组件中重写这些方法,以在组件的不同生命周期阶段执行特定的逻辑。

例如,开发者可以在componentDidMount方法中执行数据获取操作,或者在componentWillUnmount方法中执行清理操作。这些重写的方法构成了模板方法模式中的具体步骤,而React组件的生命周期框架则相当于模板方法模式中的算法框架。

Vue组件中的钩子函数

Vue.js也提供了类似的钩子函数(如createdmountedupdateddestroyed等),这些钩子函数定义了一个Vue组件实例的生命周期中的关键时刻。开发者可以在这些钩子函数中编写自己的逻辑,以在组件的不同阶段执行特定的操作。

这些钩子函数同样构成了模板方法模式中的具体步骤,而Vue组件的生命周期框架则相当于模板方法模式中的算法框架。通过重写这些钩子函数,开发者可以在不改变Vue组件生命周期框架的情况下,定制组件的行为。

前端路由框架中的导航守卫

在前端路由框架(如Vue Router、React Router)中,导航守卫(或路由守卫)提供了一种机制,允许开发者在路由跳转过程中执行特定的逻辑。这些导航守卫通常定义在路由配置中,并且可以在路由跳转的不同阶段(如进入前、进入后、离开前等)执行。

开发者可以通过实现这些导航守卫的回调函数来定制路由跳转的行为,这些回调函数构成了模板方法模式中的具体步骤。而前端路由框架中的导航守卫框架则相当于模板方法模式中的算法框架。通过实现这些回调函数,开发者可以在不改变路由框架的情况下,定制路由跳转的逻辑。

总结

模板方法模式在前端框架中的应用主要体现在以下几个方面:

  • 定义算法框架:前端框架通过定义一系列生命周期方法、钩子函数或导航守卫等,为开发者提供了一个算法的框架。
  • 实现具体步骤:开发者通过重写这些方法或实现这些回调函数,来定制算法的具体步骤。
  • 提高代码复用性和可扩展性:通过模板方法模式,前端框架可以确保算法的整体结构不变,同时允许开发者通过重写具体步骤来扩展算法的行为,从而提高了代码的复用性和可扩展性。

这些应用实例展示了模板方法模式在前端框架中的灵活性和实用性,使得开发者能够在不改变框架整体结构的情况下,轻松地定制和扩展组件或应用的行为。

2、Spring框架中JdbcTemplate

  • Spring的JdbcTemplate是模板方法模式的一个很好的应用。它提供了执行SQL操作的模板方法,如query方法用于查询数据库。
java 复制代码
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.util.List;
import java.util.Map;
// 配置数据源
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
// 创建JdbcTemplate实例
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 执行查询操作,这里的query方法是模板方法
List<Map<String, Object>> results = jdbcTemplate.query("SELECT * FROM users", (rs, rowNum) -> {
    Map<String, Object> row = new java.util.HashMap<>();
    row.put("id", rs.getInt("id"));
    row.put("name", rs.getString("name"));
    return row;
});
for (Map<String, Object> result : results) {
    System.out.println(result);
}
  • 在这个例子中,JdbcTemplatequery方法内部定义了数据库操作的基本步骤,如获取数据库连接、执行SQL语句、处理结果集等。用户只需要提供查询的SQL语句和结果集的映射逻辑(通过RowMapper接口实现),这就类似于模板方法模式中的具体步骤由子类(用户代码)来实现。

六、 应用场景

模板方法模式适用于以下场景:

javascript 复制代码
算法的整体步骤是固定的,但某些步骤的具体实现可能因情况而异。
需要将公共的代码逻辑提取到父类中,以减少代码重复。
需要在不修改算法框架的情况下扩展算法的某些部分。
  1. 框架开发:框架通常需要定义一些通用的算法流程,同时允许用户在特定的点上进行定制。例如,Web框架中处理HTTP请求的流程可以用模板方法模式来定义,框架定义了接收请求、解析请求、调用业务逻辑、返回响应等基本步骤,用户可以通过实现特定的接口来定制业务逻辑部分。
  2. 游戏开发:游戏中的角色行为可以使用模板方法模式。比如角色的移动行为,抽象类可以定义移动的基本步骤,如检查地形是否可行、更新角色位置等,具体的角色子类(如战士、法师)可以根据自己的特点实现不同的移动方式,如战士可能有冲锋技能,法师可能有瞬移技能,这些特殊的移动方式可以在子类的特定步骤中实现。

七、优缺点

优点

  1. 代码复用性高:算法的骨架在抽象类中定义,多个子类可以复用这个骨架,减少了代码重复。例如,在咖啡制作的例子中,研磨和冲泡咖啡的基本步骤可以在多个咖啡类型中复用。
  2. 可维护性好:如果需要修改算法的整体流程,只需要在抽象类的模板方法中修改,而不需要在每个子类中修改。例如,如果要在制作咖啡的流程中添加一个新的公共步骤,如预热杯子,只需要在抽象咖啡类的模板方法中添加即可。
  3. 扩展性强:通过创建新的子类,可以很容易地扩展算法的具体实现。比如要添加一种新的咖啡类型,只需要创建一个新的咖啡子类,实现抽象类中的抽象方法即可。

缺点

  1. 可能导致类层次结构复杂:如果有过多的子类来实现不同的具体步骤,可能会使类层次结构变得复杂,难以理解。例如,如果有几十种不同的咖啡类型,那么对应的咖啡子类就会很多,增加了代码的复杂度。
  2. 不符合开闭原则(部分情况):在抽象类中定义的模板方法是固定的算法流程,有时候可能需要修改这个流程,这可能会导致对抽象类的修改,不符合开闭原则(对扩展开放,对修改关闭)。不过可以通过一些设计技巧,如将模板方法中的部分步骤提取成可插拔的策略模式等来缓解这个问题。
相关推荐
拉不动的猪几秒前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
Asthenia041235 分钟前
Spring扩展点与工具类获取容器Bean-基于ApplicationContextAware实现非IOC容器中调用IOC的Bean
后端
FreeCultureBoy42 分钟前
macOS 命令行 原生挂载 webdav 方法
前端
bobz9651 小时前
ovs patch port 对比 veth pair
后端
Asthenia04121 小时前
Java受检异常与非受检异常分析
后端
uhakadotcom1 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom1 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom1 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom1 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
JavaGuide1 小时前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql