多文件格式(例如CSV、XLSX)的导入能力,可以使用模板方法模式和策略模式的组合来优化代码结构和提高执行效率
引入了 Apache POI 相关的依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>4.1.2</version>
</dependency>
1. 定义抽象模板类
首先,定义一个抽象的 ContractImporter
类,其中包含导入文件的基本流程。
/**
* ContractImporter 是一个抽象类,定义了导入合同的基本流程。
* 具体的子类需要实现 readFile 和 parseData 方法,以处理不同格式的文件。
*/
public abstract class ContractImporter {
/**
* 导入合同文件的主方法。读取文件,解析数据,并保存合同。
*
* @param filePath 要导入的合同文件的路径
*/
public void importContract(String filePath) {
// 读取文件内容,返回数据列表
List<String[]> data = readFile(filePath);
// 解析数据,返回合同列表
List<Contract> contracts = parseData(data);
// 保存合同数据
saveContracts(contracts);
}
/**
* 读取文件内容的方法。需要子类实现。
* 该方法应该读取文件并返回数据列表,每个元素表示文件中的一行数据。
*
* @param filePath 要读取的文件路径
* @return 文件内容的数据列表
*/
protected abstract List<String[]> readFile(String filePath);
/**
* 解析数据的方法。需要子类实现。
* 该方法应该解析 readFile 方法返回的数据列表,并将其转换为合同对象的列表。
*
* @param data 由 readFile 方法返回的数据列表
* @return 合同对象的列表
*/
protected abstract List<Contract> parseData(List<String[]> data);
/**
* 保存合同数据的方法。
* 该方法默认将合同信息输出到控制台,具体保存逻辑可以在子类中覆盖。
*
* @param contracts 要保存的合同对象列表
*/
private void saveContracts(List<Contract> contracts) {
// 遍历合同列表并保存每个合同
for (Contract contract : contracts) {
// 打印保存的合同信息
System.out.println("Saving contract: " + contract);
}
}
}
2. 定义 Contract
类
定义一个简单的 Contract
类,用于表示销售合同实体类。
public class Contract {
private String id;
private String name;
private String amount;
private String date;
public Contract(String id, String name, String amount, String date) {
this.id = id;
this.name = name;
this.amount = amount;
this.date = date;
}
@Override
public String toString() {
return "Contract{id='" + id + "', name='" + name + "', amount='" + amount + "', date='" + date + "'}";
}
}
3. 实现具体的策略类
为每种文件格式实现具体的导入策略类。
CSVContractImporter 类
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class CSVContractImporter extends ContractImporter {
@Override
protected List<String[]> readFile(String filePath) {
List<String[]> data = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line.split(","));
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
@Override
protected List<Contract> parseData(List<String[]> data) {
List<Contract> contracts = new ArrayList<>();
for (int i = 1; i < data.size(); i++) { // 假设第一行是表头
String[] row = data.get(i);
Contract contract = new Contract(row[0], row[1], row[2], row[3]);
contracts.add(contract);
}
return contracts;
}
}
XLSXContractImporter 类
public class XLSXContractImporter extends ContractImporter {
@Override
protected List<String[]> readFile(String filePath) {
List<String[]> data = new ArrayList<>();
try (FileInputStream fis = new FileInputStream(filePath)) {
Workbook workbook = WorkbookFactory.create(fis);
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
int numCells = row.getPhysicalNumberOfCells();
String[] rowData = new String[numCells];
for (int i = 0; i < numCells; i++) {
rowData[i] = row.getCell(i).toString();
}
data.add(rowData);
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
@Override
protected List<Contract> parseData(List<String[]> data) {
List<Contract> contracts = new ArrayList<>();
for (int i = 1; i < data.size(); i++) { // 假设第一行是表头
String[] row = data.get(i);
Contract contract = new Contract(row[0], row[1], row[2], row[3]);
contracts.add(contract);
}
return contracts;
}
}
TXTContractImporter
public class TXTContractImporter extends ContractImporter {
@Override
protected List<String[]> readFile(String filePath) {
List<String[]> data = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line.split("\t")); // 假设TXT文件以制表符分隔
}
} catch (IOException e) {
e.printStackTrace();
}
return data;
}
@Override
protected List<Contract> parseData(List<String[]> data) {
List<Contract> contracts = new ArrayList<>();
for (int i = 1; i < data.size(); i++) { // 假设第一行是表头
String[] row = data.get(i);
Contract contract = new Contract(row[0], row[1], row[2], row[3]);
contracts.add(contract);
}
return contracts;
}
}
4. 使用策略模式进行导入
public class ContractImporterClient {
public static void main(String[] args) {
// 这里使用文件路径来演示
String csvFilePath = "D:\\A-简历\\简历项目总结\\策略模式测试文件\\test.csv";
String xlsxFilePath = "D:\\A-简历\\简历项目总结\\策略模式测试文件\\test.xlsx";
String txtFilePath = "D:\\A-简历\\简历项目总结\\策略模式测试文件\\test.txt";
importFile(csvFilePath);
importFile(xlsxFilePath);
importFile(txtFilePath);
}
private static void importFile(String filePath) {
ContractImporter importer = ContractImporterStrategy.getImporter(filePath);
if (importer != null) {
importer.importContract(filePath);
} else {
System.out.println("No importer found for file: " + filePath);
}
}
}
5. 枚举类:管理不同文件类型的导入策略。消除if-else
/**
* 枚举类:管理不同文件类型的导入策略。
*/
public enum ContractImporterStrategy {
// 枚举常量,每个常量都对应一个具体的导入策略实现
CSV(new CSVContractImporter()),
XLSX(new XLSXContractImporter()),
TXT(new TXTContractImporter());
// 静态 Map,用于存储文件扩展名与枚举常量的映射关系
private static final Map<String, ContractImporterStrategy> BY_EXTENSION = new HashMap<>();
static {
// 初始化 Map,将每个枚举常量的名称(小写)与其对应的枚举常量映射起来
for (ContractImporterStrategy strategy : values()) {
BY_EXTENSION.put(strategy.name().toLowerCase(), strategy);
}
}
// 枚举常量对应的导入策略实例
private final ContractImporter importer;
/**
* 枚举类的构造方法,用于初始化导入策略实例。
*
* @param importer 具体的合同导入策略实例
*/
ContractImporterStrategy(ContractImporter importer) {
this.importer = importer;
}
/**
* 根据文件路径获取相应的导入策略。
*
* @param filePath 文件路径
* @return 对应的导入策略实例
*/
public static ContractImporter getImporter(String filePath) {
String extension = getFileExtension(filePath);
ContractImporterStrategy strategy = BY_EXTENSION.get(extension);
return strategy != null ? strategy.importer : null;
}
/**
* 从文件路径中提取文件扩展名。
*
* @param filePath 文件路径
* @return 文件扩展名(小写)
*/
private static String getFileExtension(String filePath) {
if (filePath == null || !filePath.contains(".")) {
return "";
}
return filePath.substring(filePath.lastIndexOf(".") + 1).toLowerCase();
}
}
测试文件的内容
id name amount date
1 Contract A 1000 2023/1/1
2 Contract B 2000 2023/2/1
3 Contract C 3000 2023/3/1
4 Contract D 4000 2023/4/1
5 Contract E 5000 2023/5/1
在这个示例中,我们使用了模板方法模式定义了导入文件的基本流程,并使用策略模式为不同的文件格式实现了具体的导入逻辑。这种设计不仅提高了代码的可维护性和扩展性,还能通过优化具体的导入逻辑来提高执行效率。
流程总结
在这个示例中,我们通过结合模板方法模式和策略模式来实现不同文件格式(CSV、XLSX、TXT)的合同导入功能。以下是详细的流程总结:
-
模板方法模式:
-
抽象类定义:定义了一个抽象类
ContractImporter
,其中包含导入合同的基本流程:读取文件、解析数据和保存合同。这些步骤在抽象类中定义为抽象方法或具体方法。 -
具体实现:针对不同文件格式(CSV、XLSX、TXT),分别实现了具体的导入逻辑。在
CSVContractImporter
、XLSXContractImporter
和TXTContractImporter
类中,实现了如何读取对应格式的文件和解析数据。这些类继承自ContractImporter
并实现了其抽象方法。
-
-
策略模式:
-
枚举定义:使用
ContractImporterStrategy
枚举定义支持的文件格式及其对应的导入策略。每个枚举常量(如 CSV、XLSX、TXT)都关联了一个具体的ContractImporter
实例。 -
动态选择:通过文件路径的扩展名动态选择相应的导入策略。使用静态方法
getImporter
根据文件路径的扩展名从枚举中获取对应的导入策略。 -
映射关系:使用
Map
存储文件扩展名与导入策略的映射关系。枚举类在静态代码块中初始化这个映射关系,使得策略的选择过程高效且易于扩展。
-