妙解设计模式之模板模式

模板模式的概念

模板模式 是一种行为型设计模式,它定义了一个操作的算法骨架,将某些步骤的实现延迟到子类中。通过模板模式,子类可以不改变算法的结构即可重新定义算法中的某些步骤。

简单来说,模板模式让你在一个方法中定义好算法的整体步骤,而具体每个步骤的实现可以由子类来完成。这样可以避免重复代码,并且使算法结构更加清晰。

模板模式的结构

模板模式一般包括以下角色:

抽象类(Abstract Class):定义算法的骨架,并声明一些抽象方法,这些抽象方法由子类实现。

具体子类(Concrete Class):实现抽象类中的抽象方法,从而完成算法中的具体步骤。

为什么需要模板模式

**代码复用:**模板模式可以将通用的算法步骤定义在一个父类中,避免了在每个子类中重复编写相同的代码。例如,在前面的例子中,烧水和倒入杯中的步骤是所有饮料制作过程中都需要的,所以这部分代码可以放在抽象类中,子类不需要重复实现。

**控制算法的结构:**模板模式让你可以控制算法的整体结构,而将细节实现留给子类去完成。这样即使在子类中改变了某些步骤的实现,算法的整体流程仍然保持不变。这保证了代码的稳定性和一致性。

**灵活应对变化:**模板模式允许你在不改变算法结构的前提下,通过子类来改变某些步骤的实现。这使得系统更加灵活,能够轻松应对需求的变化。例如,如果需要添加一种新类型的饮料,只需要创建一个新的子类实现相应的步骤,而不需要修改现有的代码。

**避免代码的重复和复杂性:**在一个复杂的系统中,如果没有模板模式,可能会出现许多重复的代码,或者需要在多个地方维护类似的算法结构。这不仅增加了代码的复杂性,还容易引入错误。模板模式通过将通用的部分抽取出来,减少了代码的重复,提高了代码的可维护性。

**易于维护:**模板模式将具体步骤的实现与算法的结构分离开来,使得代码更容易理解和维护。当你需要修改某个步骤时,只需在相应的子类中进行修改,而不必担心影响到其他部分。

##生活中的例子

假设我们有一个制作咖啡和茶的过程。制作咖啡和茶的步骤大致相同:烧水、冲泡、倒入杯中、加入调味料(糖、牛奶或者其它)。但每一步中可能有一些细节不同。我们可以用模板模式来抽象出这个过程。

java 复制代码
// 抽象类
abstract class Beverage {
    // 模板方法
    final void prepareRecipe() {
        boilWater();
        brew();
        pourInCup();
        addCondiments();
    }

    abstract void brew(); // 冲泡的步骤,由子类实现
    abstract void addCondiments(); // 加调味料的步骤,由子类实现

    void boilWater() {
        System.out.println("冲开水");
    }

    void pourInCup() {
        System.out.println("倒入杯子中");
    }
}

// 具体子类 - 咖啡
class Coffee extends Beverage {
    void brew() {
        System.out.println("用过滤器冲泡咖啡");
    }

    void addCondiments() {
        System.out.println("加糖");
    }
}

// 具体子类 - 茶
class Tea extends Beverage {
    void brew() {
        System.out.println("泡茶");
    }

    void addCondiments() {
        System.out.println("加点菊花");
    }
}

// 测试模板模式
public class Main {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        coffee.prepareRecipe();

        Beverage tea = new Tea();
        tea.prepareRecipe();
    }
}

编程中的例子

假设你需要开发一个系统来处理不同类型的文件(如CSV、XML、JSON)。每种文件的处理过程有些相似 ,但也有各自的特点。比如,处理每个文件的步骤包括:读取文件、解析文件、处理数据和保存结果。为了避免重复代码和增强系统的扩展性,可以使用模板模式。

步骤1:定义抽象类 FileProcessor

首先,我们创建一个抽象类 FileProcessor,定义文件处理的模板方法以及抽象方法。

java 复制代码
abstract class FileProcessor {
    // 模板方法,定义文件处理的步骤
    final void processFile(String filePath) {
        readFile(filePath);
        parseFile();
        processData();
        saveResult();
    }

    // 读取文件
    void readFile(String filePath) {
        System.out.println("读取文件" + filePath);
    }

    // 抽象方法,由子类实现解析逻辑
    abstract void parseFile();

    // 抽象方法,由子类实现数据处理逻辑
    abstract void processData();

    // 保存结果
    void saveResult() {
        System.out.println("保存结果");
    }
}

步骤2:实现具体子类

现在,我们为每种文件类型实现具体的子类。

处理CSV文件的子类

java 复制代码
class CSVFileProcessor extends FileProcessor {
    void parseFile() {
        System.out.println("解析CSV文件");
    }

    void processData() {
        System.out.println("处理CSV文件中的数据");
    }
}

处理XML文件的子类

java 复制代码
class XMLFileProcessor extends FileProcessor {
    void parseFile() {
        System.out.println("解析XML文件");
    }

    void processData() {
        System.out.println("处理XML文件中的数据");
    }
}

处理JSON文件的子类

java 复制代码
class JSONFileProcessor extends FileProcessor {
    void parseFile() {
        System.out.println("解析JSON文件");
    }

    void processData() {
        System.out.println("处理JSON文件中的数据");
    }
}

步骤3:使用模板模式处理不同类型的文件

我们可以通过创建不同的子类实例来处理不同类型的文件,而不需要重复编写读取文件、保存结果等相同的步骤。

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 处理CSV文件
        FileProcessor csvProcessor = new CSVFileProcessor();
        csvProcessor.processFile("data.csv");

        // 处理XML文件
        FileProcessor xmlProcessor = new XMLFileProcessor();
        xmlProcessor.processFile("data.xml");

        // 处理JSON文件
        FileProcessor jsonProcessor = new JSONFileProcessor();
        jsonProcessor.processFile("data.json");
    }
}

软件工程中的实际应用

框架设计

框架通常提供了某种算法或操作的整体结构,但允许开发人员在其中插入自定义的实现。例如,在一个Web应用框架中,框架可能会定义一个请求处理的模板方法,处理流程包括请求的解析、业务逻辑处理、视图渲染等。开发人员只需在特定的步骤中插入自己的业务逻辑,而不需要改变整体的请求处理流程。

例子:Spring Framework中的模板方法

在Spring Framework中,JdbcTemplate是一个常见的例子。JdbcTemplate提供了一个执行数据库查询的通用模板,而用户只需要实现特定的查询处理逻辑。

java 复制代码
public void execute() {
    try {
        // 通用的打开连接、处理事务等步骤
        executeStatement(); // 具体的执行逻辑,由用户定义
    } catch (SQLException ex) {
        // 错误处理
    } finally {
        // 通用的资源释放步骤
    }
}

代码复用

在大型系统中,不同模块或组件可能会涉及到类似的操作逻辑,但某些步骤会有所不同。模板模式允许将这些通用步骤抽象出来,减少代码重复,提高复用性。

例子:文件导入导出功能

在数据导入导出系统中,不同的数据源(如CSV、Excel、数据库)可能需要相似的处理步骤:读取数据、解析数据、存储数据。可以通过模板模式将这些通用步骤抽象出来,而让具体的数据源处理方式在子类中实现。

测试框架

测试框架中,尤其是单元测试框架中,模板模式被广泛应用。测试流程通常包括:设置测试环境、执行测试、清理测试环境。不同的测试用例可能需要不同的设置和清理步骤,但执行测试的流程是固定的。

例子:JUnit测试框架

在JUnit框架中,测试类通常继承自一个测试基类。测试基类提供了测试执行的模板方法,而具体的测试逻辑和初始化、清理逻辑由子类实现。

java 复制代码
public abstract class TestCase {
    public void run() {
        setUp();   // 设置测试环境
        try {
            runTest();  // 运行具体测试方法
        } finally {
            tearDown(); // 清理测试环境
        }
    }

    protected abstract void runTest();
    protected void setUp() {}
    protected void tearDown() {}
}

算法设计

在一些复杂的算法实现中,算法的整体结构是固定的,但某些步骤可能会根据具体情况有所不同。模板模式允许这些步骤的实现细节由子类来定义,而不改变算法的整体框架。

例子:排序算法

考虑一个需要支持多种排序方式的排序系统,系统中可以定义一个通用的排序模板方法,而让具体的排序算法(如快速排序、归并排序)在子类中实现。

java 复制代码
abstract class Sorter {
    public void sort(int[] array) {
        // 通用的排序准备步骤
        sortArray(array);  // 具体的排序算法
        // 通用的排序完成步骤
    }

    protected abstract void sortArray(int[] array);
}

工作流引擎

工作流引擎通常需要处理一系列的步骤,如审批流程、任务分配等。这些流程的步骤顺序是固定的,但不同的工作流可能会在某些步骤上有不同的处理逻辑。模板模式可以帮助定义工作流的整体框架,并允许具体的工作流子类自定义特定步骤的实现。

例子:审批流程

在一个审批系统中,不同的审批流程(如请假审批、报销审批)可能共享某些通用步骤(如提交申请、初审、复审),但每个步骤的具体实现可能不同。通过模板模式,可以将审批流程的整体框架定义在父类中,而让不同的审批类型在子类中实现各自的逻辑。

游戏开发

在游戏开发中,模板模式可以用于定义通用的游戏对象行为或事件处理流程。例如,角色的行为可能包括初始化、执行动作、清理资源等步骤。这些步骤可以作为一个模板方法,具体的角色类型(如敌人、玩家)可以在子类中实现各自的动作逻辑。

例子:角色行为

java 复制代码
abstract class GameCharacter {
    public void performAction() {
        initialize();  // 初始化
        execute();     // 执行动作
        cleanUp();     // 清理资源
    }

    protected abstract void initialize();
    protected abstract void execute();
    protected abstract void cleanUp();
}
相关推荐
计算机学姐几秒前
基于SSM的宠物领养平台
java·vue.js·spring·maven·intellij-idea·mybatis·宠物
泰山小张只吃荷园11 分钟前
期末Python复习-输入输出
java·前端·spring boot·python·spring cloud·docker·容器
Mr_Xuhhh13 分钟前
程序地址空间
android·java·开发语言·数据库
YSRM19 分钟前
异或-java-leetcode
java·算法·leetcode
大明湖的狗凯.22 分钟前
MySQL 中的乐观锁与悲观锁
java·数据库·mysql
z2023050828 分钟前
linux之调度管理(13)- wake affine 唤醒特性
java·开发语言
AI人H哥会Java29 分钟前
【JAVA】Java高级:Java网络编程——TCP/IP与UDP协议基础
java·开发语言
robin_suli1 小时前
Java多线程八股(三)一>多线程环境使用哈希表和ArrayList
java·开发语言·多线程·哈希表
NiNg_1_2341 小时前
Java中的多线程
java·开发语言
不爱说话郭德纲1 小时前
Stylus、Less 和 Sass 的使用与区别
前端·css·面试·less·sass·stylus