1. 项目结构
-
项目结构
bashtemplate-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 -
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. 代码实现
-
抽象模板类 (DataProcessor.java)
javapackage 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("验证数据..."); // 数据验证逻辑 } } } -
具体实现类 - CSV处理器 (CSVDataProcessor.java)
javapackage 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处理器额外清理工作完成"); } } -
具体实现类 - XML处理器 (XMLDataProcessor.java)
javapackage 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]; }); } } -
具体实现类 - JSON处理器 (JsonDataProcessor.java)
javapackage 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] + ")"; }); } } -
示例数据文件
在 src/main/resources/ 目录下创建以下文件:
data.csv:bashid,name,value 1,John Doe,100 2,Jane Smith,200 3,Bob Johnson,150data.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用户" } ] } -
演示类 (Demo.java)
javapackage 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=== 演示完成 ==="); } } -
单元测试类 (DataProcessorTest.java)
javapackage 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. 构建和运行
-
构建项目
bashmvn clean compile -
运行程序
bashmvn exec:java -Dexec.mainClass="com.example.templatepattern.Demo" -
运行测试
bashmvn test -
打包项目
bashmvn package
4. 核心概念
模板模式的核心要点
- 模板方法:DataProcessor.processData() 是模板方法,定义了算法的骨架
- 抽象方法:parseData() 和 processDataLogic() 必须由子类实现
- 具体方法:readFile()、saveResult() 有默认实现
- 钩子方法:needValidation() 和 cleanup() 子类可选择覆盖
- 好莱坞原则:"不要调用我们,我们会调用你" - 父类控制流程,子类提供具体实现
优势
- 代码复用:公共逻辑在父类中实现
- 扩展性好:新的数据格式只需创建新的子类
- 符合开闭原则:对扩展开放,对修改关闭
- 控制反转:父类控制算法流程