行为型-模板模式

1. 项目结构

  1. 项目结构

    bash 复制代码
    	template-pattern-demo/
    	├── pom.xml
    	├── src/
    	│   ├── main/
    	│   │   ├── java/
    	│   │   │   └── com/
    	│   │   │       └── example/
    	│   │   │           └── templatepattern/
    	│   │   │               ├── DataProcessor.java
    	│   │   │               ├── CSVDataProcessor.java
    	│   │   │               ├── XMLDataProcessor.java
    	│   │   │               ├── JsonDataProcessor.java
    	│   │   │               └── Demo.java
    	│   │   └── resources/
    	│   │       ├── data.csv
    	│   │       ├── data.xml
    	│   │       └── data.json
    	└── test/
    	    └── java/
    	        └── com/
    	            └── example/
    	                └── templatepattern/
    	                    └── DataProcessorTest.java
  2. Maven 配置文件 (pom.xml)

    bash 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
             http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.example</groupId>
        <artifactId>template-pattern-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <!-- 用于解析JSON文件 -->
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.13.3</version>
            </dependency>
            
            <!-- 用于XML解析 -->
            <dependency>
                <groupId>javax.xml.parsers</groupId>
                <artifactId>jaxp-api</artifactId>
                <version>1.4.5</version>
            </dependency>
            
            <!-- 单元测试 -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>

2. 代码实现

  1. 抽象模板类 (DataProcessor.java)

    java 复制代码
    package com.example.templatepattern;
    
    import java.io.File;
    import java.util.List;
    
    /**
     * 抽象模板类 - 数据处理器
     * 定义了数据处理的标准流程
     */
    public abstract class DataProcessor {
        
        /**
         * 模板方法 - 定义了算法的骨架
         * 该方法声明为final,防止子类改变算法结构
         */
        public final void processData(String filePath) {
            // 1. 读取文件
            File file = readFile(filePath);
            
            // 2. 解析数据
            List<String> data = parseData(file);
            
            // 3. 处理数据(由子类实现具体逻辑)
            processDataLogic(data);
            
            // 4. 保存结果
            saveResult(data);
            
            // 5. 清理资源
            cleanup(file);
        }
        
        /**
         * 具体步骤1:读取文件(通用实现)
         */
        private File readFile(String filePath) {
            System.out.println("读取文件: " + filePath);
            File file = new File(filePath);
            if (!file.exists()) {
                throw new RuntimeException("文件不存在: " + filePath);
            }
            return file;
        }
        
        /**
         * 抽象方法:解析数据
         * 必须由子类实现
         */
        protected abstract List<String> parseData(File file);
        
        /**
         * 抽象方法:处理数据逻辑
         * 必须由子类实现
         */
        protected abstract void processDataLogic(List<String> data);
        
        /**
         * 具体步骤4:保存结果(通用实现)
         */
        private void saveResult(List<String> data) {
            System.out.println("保存处理结果,共 " + data.size() + " 条记录");
            // 实际项目中这里可能是保存到数据库或写入文件
        }
        
        /**
         * 钩子方法:清理资源
         * 子类可以选择性地覆盖此方法
         */
        protected void cleanup(File file) {
            System.out.println("清理资源: " + file.getName());
            // 默认不执行任何操作,子类可以覆盖
        }
        
        /**
         * 钩子方法:是否需要验证数据
         * 子类可以选择性地覆盖此方法
         */
        protected boolean needValidation() {
            return false; // 默认不需要验证
        }
        
        /**
         * 具体方法:验证数据
         * 由钩子方法控制是否执行
         */
        private void validateData(List<String> data) {
            if (needValidation()) {
                System.out.println("验证数据...");
                // 数据验证逻辑
            }
        }
    }
  2. 具体实现类 - CSV处理器 (CSVDataProcessor.java)

    java 复制代码
    package com.example.templatepattern;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 具体实现类 - CSV数据处理器
     */
    public class CSVDataProcessor extends DataProcessor {
        
        @Override
        protected List<String> parseData(File file) {
            System.out.println("解析CSV文件: " + file.getName());
            List<String> data = new ArrayList<>();
            
            try (BufferedReader br = new BufferedReader(new FileReader(file))) {
                String line;
                br.readLine(); // 跳过表头
                while ((line = br.readLine()) != null) {
                    data.add(line);
                }
            } catch (Exception e) {
                throw new RuntimeException("解析CSV文件失败", e);
            }
            
            return data;
        }
        
        @Override
        protected void processDataLogic(List<String> data) {
            System.out.println("处理CSV数据,转换为对象列表");
            // 实际业务逻辑:将CSV行转换为业务对象
            data.replaceAll(line -> {
                String[] parts = line.split(",");
                return "CSV记录: " + parts[0] + " - " + parts[1];
            });
        }
        
        @Override
        protected boolean needValidation() {
            return true; // CSV处理器需要数据验证
        }
        
        @Override
        protected void cleanup(File file) {
            super.cleanup(file);
            System.out.println("CSV处理器额外清理工作完成");
        }
    }
  3. 具体实现类 - XML处理器 (XMLDataProcessor.java)

    java 复制代码
    package com.example.templatepattern;
    
    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import org.w3c.dom.Document;
    import org.w3c.dom.Element;
    import org.w3c.dom.NodeList;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 具体实现类 - XML数据处理器
     */
    public class XMLDataProcessor extends DataProcessor {
        
        @Override
        protected List<String> parseData(File file) {
            System.out.println("解析XML文件: " + file.getName());
            List<String> data = new ArrayList<>();
            
            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document document = builder.parse(file);
                
                NodeList itemList = document.getElementsByTagName("item");
                for (int i = 0; i < itemList.getLength(); i++) {
                    Element item = (Element) itemList.item(i);
                    String id = item.getAttribute("id");
                    String name = item.getElementsByTagName("name").item(0).getTextContent();
                    String value = item.getElementsByTagName("value").item(0).getTextContent();
                    data.add(id + "|" + name + "|" + value);
                }
            } catch (Exception e) {
                throw new RuntimeException("解析XML文件失败", e);
            }
            
            return data;
        }
        
        @Override
        protected void processDataLogic(List<String> data) {
            System.out.println("处理XML数据,转换为对象列表");
            // 实际业务逻辑:将XML数据转换为业务对象
            data.replaceAll(line -> {
                String[] parts = line.split("\\|");
                return "XML记录: ID=" + parts[0] + ", Name=" + parts[1];
            });
        }
    }
  4. 具体实现类 - JSON处理器 (JsonDataProcessor.java)

    java 复制代码
    package com.example.templatepattern;
    
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 具体实现类 - JSON数据处理器
     */
    public class JsonDataProcessor extends DataProcessor {
        
        private final ObjectMapper objectMapper = new ObjectMapper();
        
        @Override
        protected List<String> parseData(File file) {
            System.out.println("解析JSON文件: " + file.getName());
            List<String> data = new ArrayList<>();
            
            try {
                JsonNode rootNode = objectMapper.readTree(file);
                JsonNode itemsNode = rootNode.get("items");
                
                if (itemsNode != null && itemsNode.isArray()) {
                    for (JsonNode itemNode : itemsNode) {
                        String id = itemNode.get("id").asText();
                        String name = itemNode.get("name").asText();
                        String type = itemNode.get("type").asText();
                        data.add(id + "," + name + "," + type);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("解析JSON文件失败", e);
            }
            
            return data;
        }
        
        @Override
        protected void processDataLogic(List<String> data) {
            System.out.println("处理JSON数据,转换为对象列表");
            // 实际业务逻辑:将JSON数据转换为业务对象
            data.replaceAll(line -> {
                String[] parts = line.split(",");
                return "JSON记录: " + parts[1] + " (" + parts[2] + ")";
            });
        }
    }
  5. 示例数据文件

    在 src/main/resources/ 目录下创建以下文件:
    data.csv:

    bash 复制代码
    id,name,value
    1,John Doe,100
    2,Jane Smith,200
    3,Bob Johnson,150

    data.xml:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <data>
        <item id="101">
            <name>产品A</name>
            <value>29.99</value>
        </item>
        <item id="102">
            <name>产品B</name>
            <value>49.99</value>
        </item>
        <item id="103">
            <name>产品C</name>
            <value>19.99</value>
        </item>
    </data>

    data.json:

    javascript 复制代码
    {
      "items": [
        {
          "id": "user001",
          "name": "张三",
          "type": "管理员"
        },
        {
          "id": "user002",
          "name": "李四",
          "type": "普通用户"
        },
        {
          "id": "user003",
          "name": "王五",
          "type": "VIP用户"
        }
      ]
    }
  6. 演示类 (Demo.java)

    java 复制代码
    package com.example.templatepattern;
    
    /**
     * 模板模式演示类
     */
    public class Demo {
        
        public static void main(String[] args) {
            System.out.println("=== 模板模式演示 ===");
            
            // 获取资源文件路径
            String csvPath = Demo.class.getClassLoader().getResource("data.csv").getPath();
            String xmlPath = Demo.class.getClassLoader().getResource("data.xml").getPath();
            String jsonPath = Demo.class.getClassLoader().getResource("data.json").getPath();
            
            System.out.println("\n1. 处理CSV数据:");
            DataProcessor csvProcessor = new CSVDataProcessor();
            csvProcessor.processData(csvPath);
            
            System.out.println("\n2. 处理XML数据:");
            DataProcessor xmlProcessor = new XMLDataProcessor();
            xmlProcessor.processData(xmlPath);
            
            System.out.println("\n3. 处理JSON数据:");
            DataProcessor jsonProcessor = new JsonDataProcessor();
            jsonProcessor.processData(jsonPath);
            
            System.out.println("\n=== 演示完成 ===");
        }
    }
  7. 单元测试类 (DataProcessorTest.java)

    java 复制代码
    package com.example.templatepattern;
    
    import org.junit.Test;
    import static org.junit.Assert.*;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.util.List;
    
    public class DataProcessorTest {
        
        @Test
        public void testCSVDataProcessor() {
            // 创建测试CSV文件
            File testFile = createTestCSVFile();
            
            try {
                CSVDataProcessor processor = new CSVDataProcessor();
                
                // 测试解析方法
                List<String> data = processor.parseData(testFile);
                assertEquals(3, data.size());
                assertTrue(data.get(0).contains("test1"));
                
                // 测试处理逻辑
                processor.processDataLogic(data);
                assertTrue(data.get(0).startsWith("CSV记录:"));
                
            } finally {
                // 清理测试文件
                testFile.delete();
            }
        }
        
        @Test
        public void testTemplateMethodStructure() {
            File testFile = createTestCSVFile();
            
            try {
                DataProcessor processor = new CSVDataProcessor();
                
                // 验证模板方法被正确调用
                processor.processData(testFile.getPath());
                
                // 如果执行到这里没有异常,说明模板方法的流程正确
                assertTrue(true);
                
            } finally {
                testFile.delete();
            }
        }
        
        private File createTestCSVFile() {
            try {
                File file = new File("test_data.csv");
                FileWriter writer = new FileWriter(file);
                writer.write("id,name,value\n");
                writer.write("1,test1,100\n");
                writer.write("2,test2,200\n");
                writer.write("3,test3,300\n");
                writer.close();
                return file;
            } catch (Exception e) {
                throw new RuntimeException("创建测试文件失败", e);
            }
        }
    }

3. 构建和运行

  1. 构建项目

    bash 复制代码
    mvn clean compile
  2. 运行程序

    bash 复制代码
    mvn exec:java -Dexec.mainClass="com.example.templatepattern.Demo"
  3. 运行测试

    bash 复制代码
    mvn test
  4. 打包项目

    bash 复制代码
    mvn package

4. 核心概念

模板模式的核心要点

  • 模板方法:DataProcessor.processData() 是模板方法,定义了算法的骨架
  • 抽象方法:parseData() 和 processDataLogic() 必须由子类实现
  • 具体方法:readFile()、saveResult() 有默认实现
  • 钩子方法:needValidation() 和 cleanup() 子类可选择覆盖
  • 好莱坞原则:"不要调用我们,我们会调用你" - 父类控制流程,子类提供具体实现

优势

  • 代码复用:公共逻辑在父类中实现
  • 扩展性好:新的数据格式只需创建新的子类
  • 符合开闭原则:对扩展开放,对修改关闭
  • 控制反转:父类控制算法流程
相关推荐
sxlishaobin12 小时前
设计模式之原型模式
设计模式·原型模式
范纹杉想快点毕业13 小时前
嵌入式通信核心架构:从状态机、环形队列到多协议融合
linux·运维·c语言·算法·设计模式
__万波__13 小时前
二十三种设计模式(二十)--解释器模式
java·设计模式·解释器模式
攀登的牵牛花14 小时前
前端向架构突围系列 - 架构方法(一):概述 4+1 视图模型
前端·设计模式·架构
雲墨款哥14 小时前
从一行好奇的代码说起:React的 useEffect 到底是不是生命周期?
前端·react.js·设计模式
cultivator12948016 小时前
设计原则和设计模式助记
设计模式
enjoy编程17 小时前
Spring boot 4 探究netty的关键知识点
spring boot·设计模式·reactor·netty·多线程
用户938169125536017 小时前
Head First 单例模式
后端·设计模式
a35354138218 小时前
设计模式-桥接模式
c++·设计模式·桥接模式
sxlishaobin18 小时前
设计模式之外观模式
java·设计模式·外观模式