学生信息管理系统(DAO模式重构)项目报告
学生信息管理系统(DAO 模式重构)项目报告
一、项目概述
1.1 项目功能介绍
本项目是一个基于 Java 命令行界面的学生信息管理系统,旨在为用户提供对学生数据的基本增、删、改、查(CRUD)功能。系统核心功能包括:
-
添加学生:允许用户输入学生的ID、姓名、性别和年龄等信息,并将其添加到系统中。
-
删除学生:根据学生ID删除指定的学生记录。
-
修改学生:根据学生ID查找并更新该学生的信息。
-
查询学生:
-
查询所有学生列表。
-
根据学生ID进行精确查询。
-
-
数据持久化:支持将学生数据保存到本地文件(文本或Excel),并在程序启动时自动加载。
1.2 原项目结构
在重构前,项目采用简单的单体结构,所有代码文件均位于同一包下,职责划分不清晰。其典型结构如下:
src/
└── code/
├── Main.java // 程序入口,包含所有UI逻辑和业务逻辑
├── Student.java // 学生数据模型
└── StudentManagementSystem.java // 数据管理类,直接操作内存中的List
1.3 原项目不足
原项目虽然能实现基本功能,但在架构设计上存在明显缺陷,主要体现在以下三方面:
-
数据易失性:系统采用内存
List
作为唯一数据源,导致数据生命周期与程序进程绑定,不具备持久化能力,程序终止后数据即丢失。 -
架构耦合度高:数据访问逻辑与业务逻辑紧密耦合,违反了单一职责原则。这使得系统难以扩展,若要更换存储介质(如文件或数据库),则必须侵入并大幅修改现有业务代码。
-
缺乏持久化机制:系统未提供任何数据持久化方案,无法满足用户对数据长期保存和跨会话访问的基本需求,限制了其在实际场景中的应用价值。
1.4 改造方向
为解决上述问题,本次重构的核心目标是引入DAO(Data Access Object)设计模式,对系统进行分层解耦。具体改造方向如下:
-
引入分层架构:将系统划分为表示层、业务逻辑层和数据访问层,明确各层职责。
-
应用DAO模式:定义数据访问接口
StudentDao
,将数据存储的具体实现(内存、文件、Excel)与上层业务逻辑隔离。 -
实现多种持久化方案:提供基于文本文件和Excel文件的数据持久化实现,使数据能够独立于程序存在。
-
增强代码健壮性:优化异常处理和资源管理,提升系统的稳定性。
1.5 改造后的优势
通过DAO模式重构,系统获得了显著的提升:
-
低耦合:业务逻辑层不再依赖具体的数据存储实现,而是面向
StudentDao
接口编程。更换存储方式(如从文件切换到数据库)只需新增一个实现类,无需修改任何业务代码。 -
高内聚:数据访问相关的所有操作被封装在各自的DAO实现类中,职责单一,代码更易于理解和维护。
-
可扩展性强:遵循“开闭原则”(对扩展开放,对修改关闭),未来可以轻松增加新的存储方式(如JSON、XML、数据库等)。
-
可测试性高:可以轻松为
StudentDao
编写 Mock 实现,从而在不依赖真实文件或数据库的情况下,对业务逻辑层进行单元测试。
二、项目分析
2.1 结构解析
2.1.1 DAO模式项目框架
重构后的系统严格遵循DAO设计模式,其核心思想是为数据访问建立一个抽象层,将数据存储和访问的细节与业务逻辑分离开来。框架主要由以下几部分组成:
-
数据模型:
Student.java
,用于封装学生数据。 -
数据访问接口:
StudentDao.java
,定义所有数据操作的标准方法(增删改查)。 -
数据访问实现类:
StudentDaoListImpl.java
、StudentDaoFileImpl.java
、StudentDaoExcelImpl.java
,分别提供基于内存、文本文件和Excel文件的具体实现。 -
业务逻辑/表示层:
Main.java
,负责用户交互,并调用DAO接口执行业务操作。
2.1.2 包结构
重构后的项目采用了清晰的包结构来组织代码,体现了分层思想:
src/
└── main/
└── java/
└── code/
│ ├── Main.java // 程序入口,业务逻辑层
│ ├──Student.java // 学生数据模型
│ └── StudentManagementSystemTest.java
└── dao/
├── StudentDao.java // 数据访问接口
├── StudentDaoListImpl.java // 内存存储实现
│── StudentDaoFileImpl.java // 文本文件存储实现
└── StudentDaoExcelImpl.java // Excel文件存储实现
2.1.3 关系图
以下是重构后系统各组件之间的关系图,清晰地展示了分层和依赖关系:
2.2 代码解析
2.2.1 业务逻辑层
1. Main.java
Main.java
作为程序的入口和表示层,其职责被精简为:
-
初始化和展示用户菜单。
-
接收用户输入。
-
调用业务逻辑(本项目中直接调用DAO接口)。
-
展示操作结果。
关键代码片段:
// Main.java
public class Main {
public static void main(String[] args) {
// 通过工厂模式或配置决定使用哪种DAO实现
// StudentDao studentDao = new StudentDaoListImpl(); // 内存实现
StudentDao studentDao = new StudentDaoFileImpl(); // 文件实现
// StudentDao studentDao = new StudentDaoExcelImpl(); // Excel实现
Scanner scanner = new Scanner(System.in);
while (true) {
// ... 显示菜单 ...
int choice = scanner.nextInt();
switch (choice) {
case 1: // 添加学生
// ... 获取学生信息 ...
studentDao.addStudent(new Student(id, name, gender, age));
break;
case 2: // 删除学生
// ... 获取学生ID ...
studentDao.deleteStudent(id);
break;
// ... 其他case调用studentDao的方法
}
}
}
}
分析:Main.java
只持有 StudentDao
接口的引用,它完全不知道数据是存储在内存、文本文件还是Excel中。这种设计实现了业务逻辑与数据访问的完美解耦。
2.2.2 数据访问层
1. StudentDao.java
这是DAO模式的核心,它定义了所有学生数据操作的契约,是一个纯粹的接口。
点击查看代码
// StudentDao.java
public interface StudentDao {
void addStudent(Student student);
void deleteStudent(String id);
void updateStudent(Student student);
Student getStudentById(String id);
List<Student> getAllStudents();
}
2. StudentDaoListImpl.java
最简单的实现,直接操作内存中的 ArrayList
。主要用于测试和演示。
点击查看代码
package dao;
import java.util.ArrayList;
import java.util.List;
import code.Student;
public class StudentDaoListImpl implements StudentDao {
private List<Student> students;
public StudentDaoListImpl() {
students = new ArrayList<>();
}
public void addStudent(Student student) {
students.add(student);
}
public void removeStudent(Student removeStudent) {
String removeId= removeStudent.getId();
boolean removed = false;
for (Student s : students) {
if (s.getId().equals(removeId)) {
students.remove(s);
removed = true;
System.out.println("Student removed successfully!\n");
break;
}
}
if (!removed) {
System.out.println("Student not found!");
}
}
public void removeStudentId(String removeId) {
boolean removed = false;
for (Student s : students) {
if (s.getId().equals(removeId)) {
students.remove(s);
removed = true;
System.out.println("Student removed successfully!\n");
break;
}
}
if (!removed) {
System.out.println("Student not found!");
}
}
public void getStudents() {
if (students.size() == 0) {
System.out.println("The System Data is empty Now!");
}else {
for (Student studentItem : students) {
System.out.println(studentItem.toString());
}
}
}
public void searchByName(String name) {
List<Student> result = new ArrayList<>();
for (Student student : students) {
if (student.getName().equals(name)) {
result.add(student);
}
}
if (result.isEmpty()) {
System.out.println("No students found!");
} else {
System.out.println("Search results:");
for (Student s : result) {
System.out.println(s);
}
}
}
public void searchByMajor(String major) {
List<Student> result = new ArrayList<>();
for (Student student : students) {
if (student.getMajor().equals(major)) {
result.add(student);
}
}
if (result.isEmpty()) {
System.out.println("No students found!");
} else {
System.out.println("Search results:");
for (Student s : result) {
System.out.println(s);
}
}
}
public void searchByGpa(double gpa) {
List<Student> result = new ArrayList<>();
for (Student student : students) {
if (student.getGpa() == gpa) {
result.add(student);
}
}
if (result.isEmpty()) {
System.out.println("No students found!");
} else {
System.out.println("Search results:");
for (Student s : result) {
System.out.println(s);
}
}
}
}
3. StudentDaoFileImpl.java
将数据以文本行(如 id,name,gender,age
)的形式存储在 .txt
文件中。每次操作时都进行文件读写。
点击查看代码
package dao;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import code.Student;
public class StudentDaoFileImpl implements StudentDao {
private static final String FILE_NAME = "students.txt";
private Path filePath;
private List<Student> students;
public StudentDaoFileImpl() {
this.filePath = Paths.get(FILE_NAME);
this.students = new ArrayList<>();
try {
// 如果文件不存在,则创建一个空文件
if (!Files.exists(filePath)) {
Files.createFile(filePath);
return;
}
// 读取文件所有行
List<String> lines = Files.readAllLines(filePath);
students.clear(); // 清空现有数据,以防重复加载
for (String line : lines) {
// 跳过空行
if (line.trim().isEmpty()) {
continue;
}
Student student = Student.fromString(line);
if (student != null) {
students.add(student);
}
}
} catch (IOException e) {
System.err.println("Error loading students from file: " + e.getMessage());
}
}
private void saveStudentsToFile() {
try {
// 将 Student 对象列表转换为字符串行列表
List<String> lines = students.stream()
.map(Student::toString)
.collect(Collectors.toList());
// 写入文件,使用 TRUNCATE_EXISTING 选项覆盖原有内容
Files.write(filePath, lines, StandardOpenOption.TRUNCATE_EXISTING);
} catch (IOException e) {
System.err.println("Error saving students to file: " + e.getMessage());
}
}
public void addStudent(Student student) {
if (student != null) {
students.add(student);
saveStudentsToFile();
}
}
public void removeStudent(Student removeStudent) {
String removeId= removeStudent.getId();
boolean removed = students.removeIf(student -> student.getId().equals(removeId));
if (removed) {
saveStudentsToFile(); // 删除后立即保存
} else {
System.out.println("Student not found!");
}
}
public void removeStudentId(String removeId) {
boolean removed = students.removeIf(student -> student.getId().equals(removeId));
if (removed) {
saveStudentsToFile(); // 删除后立即保存
} else {
System.out.println("Student not found!");
}
}
public void getStudents() {
if (students.size() == 0) {
System.out.println("The System Data is empty Now!");
} else {
for (Student studentItem : students) {
System.out.println(studentItem.toString());
}
}
}
public void searchByName(String name) {
List<Student> result = students.stream()
.filter(s -> s.getName().equalsIgnoreCase(name))
.collect(Collectors.toList());
printSearchResults(result);
}
public void searchByMajor(String major) {
List<Student> result = students.stream()
.filter(s -> s.getMajor().equalsIgnoreCase(major))
.collect(Collectors.toList());
printSearchResults(result);
}
public void searchByGpa(double gpa) {
List<Student> result = students.stream()
.filter(s -> s.getGpa() >= gpa) // 假设是查找大于等于该GPA的学生
.collect(Collectors.toList());
printSearchResults(result);
}
private void printSearchResults(List<Student> result) {
if (result.isEmpty()) {
System.out.println("No students found!");
} else {
System.out.println("Search results:");
for (Student s : result) {
System.out.println(s);
}
}
}
}
4. StudentDaoExcelImpl.java
使用第三方库(如 Apache POI)操作 .xlsx
文件。这是本次重构中功能最复杂的实现。
点击查看代码
package dao;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import code.Student;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* StudentDao 的 Excel 文件实现类。
* 负责将学生数据持久化到 students.xlsx 文件中。
*/
public class StudentDaoExcelImpl implements StudentDao {
private static final String FILENAME = "students.xlsx";
private static final String[] HEADERS = {"ID", "Name", "Age", "Gender", "Major", "GPA"};
/**
* 从 Excel 文件加载所有学生数据到内存中的 List
* @return 包含所有学生的列表
*/
private List<Student> loadStudentsFromFile() {
List<Student> students = new ArrayList<>();
try (FileInputStream fis = new FileInputStream(FILENAME);
Workbook workbook = new XSSFWorkbook(fis)) {
Sheet sheet = workbook.getSheetAt(0);
for (int i = 1; i <= sheet.getLastRowNum(); i++) { // 从第2行开始读取(跳过表头)
Row row = sheet.getRow(i);
if (row == null) continue;
String id = row.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getStringCellValue();
String name = row.getCell(1, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getStringCellValue();
int age = (int) row.getCell(2, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getNumericCellValue();
String gender = row.getCell(3, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getStringCellValue();
String major = row.getCell(4, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getStringCellValue();
double gpa = row.getCell(5, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK).getNumericCellValue();
students.add(new Student(id, age, name, gender, major, gpa));
}
} catch (IOException e) {
// 文件不存在或为空时,返回空列表
System.err.println("Info: Could not read from file. It might be new or empty. " + e.getMessage());
}
return students;
}
/**
* 将内存中的 List<Student> 保存到 Excel 文件
* @param students 要保存的学生列表
*/
private void saveStudentsToFile(List<Student> students) {
try (Workbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(FILENAME)) {
Sheet sheet = workbook.createSheet("Students");
// 创建表头
Row headerRow = sheet.createRow(0);
for (int i = 0; i < HEADERS.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(HEADERS[i]);
}
// 填充数据
for (int i = 0; i < students.size(); i++) {
Row row = sheet.createRow(i + 1);
Student student = students.get(i);
row.createCell(0).setCellValue(student.getId());
row.createCell(1).setCellValue(student.getName());
row.createCell(2).setCellValue(student.getAge());
row.createCell(3).setCellValue(student.getGender());
row.createCell(4).setCellValue(student.getMajor());
row.createCell(5).setCellValue(student.getGpa());
}
workbook.write(fos);
} catch (IOException e) {
System.err.println("Error saving to file: " + e.getMessage());
}
}
@Override
public void addStudent(Student student) {
List<Student> students = loadStudentsFromFile();
// 检查ID是否已存在
boolean exists = students.stream().anyMatch(s -> s.getId().equals(student.getId()));
if (exists) {
System.out.println("Error: Student with ID " + student.getId() + " already exists.");
return;
}
students.add(student);
saveStudentsToFile(students);
System.out.println("Student added successfully: " + student);
}
@Override
public void removeStudent(Student removeStudent) {
List<Student> students = loadStudentsFromFile();
boolean removed = students.removeIf(s -> s.getId().equals(removeStudent.getId()));
if (removed) {
saveStudentsToFile(students);
System.out.println("Student removed successfully: " + removeStudent);
} else {
System.out.println("Error: Student not found for removal: " + removeStudent);
}
}
@Override
public void getStudents() {
System.out.println("--- All Students ---");
List<Student> students = loadStudentsFromFile();
if (students.isEmpty()) {
System.out.println("No students found.");
} else {
students.forEach(System.out::println);
}
System.out.println("--------------------");
}
@Override
public void searchByName(String name) {
System.out.println("--- Search Results by Name: " + name + " ---");
List<Student> students = loadStudentsFromFile();
List<Student> result = new ArrayList<>();
for (Student s : students) {
if (s.getName().equalsIgnoreCase(name)) {
result.add(s);
}
}
if (result.isEmpty()) {
System.out.println("No students found with the name: " + name);
} else {
result.forEach(System.out::println);
}
System.out.println("--------------------------------------");
}
@Override
public void searchByMajor(String major) {
System.out.println("--- Search Results by Major: " + major + " ---");
List<Student> students = loadStudentsFromFile();
List<Student> result = new ArrayList<>();
for (Student s : students) {
if (s.getMajor().equalsIgnoreCase(major)) {
result.add(s);
}
}
if (result.isEmpty()) {
System.out.println("No students found with the major: " + major);
} else {
result.forEach(System.out::println);
}
System.out.println("---------------------------------------");
}
@Override
public void searchByGpa(double gpa) {
System.out.println("--- Search Results by GPA >= " + gpa + " ---");
List<Student> students = loadStudentsFromFile();
List<Student> result = new ArrayList<>();
for (Student s : students) {
if (s.getGpa() >= gpa) {
result.add(s);
}
}
if (result.isEmpty()) {
System.out.println("No students found with GPA >= " + gpa);
} else {
result.forEach(System.out::println);
}
System.out.println("--------------------------------------");
}
@Override
public void removeStudentId(String removeId) {
System.out.println("--- Attempting to remove student with ID: " + removeId + " ---");
List<Student> students = loadStudentsFromFile();
boolean removed = students.removeIf(s -> s.getId().equals(removeId));
if (removed) {
saveStudentsToFile(students);
System.out.println("Student with ID " + removeId + " removed successfully.");
} else {
System.out.println("Error: Student with ID " + removeId + " not found.");
}
System.out.println("---------------------------------------------------");
}
}
2.3 代码改造难点及解决方案
2.3.1 Excel文件导入导出
难点:
-
依赖管理:需要引入第三方库(Apache POI),并正确配置。
-
API复杂性:POI的API相对复杂,需要处理工作簿、工作表、行、单元格等多个对象。
-
数据类型转换:从Excel单元格读取的数据需正确转换为Java中的
String
、int
等类型,并处理空值和格式错误。 -
资源管理与内存泄漏风险:
FileInputStream
和FileOutputStream
等资源必须在使用后正确关闭,否则会导致文件泄露或损坏。
解决方案:
-
引入 Maven 进行依赖管理:将项目改造为 Maven 项目。Maven 是一个自动化构建工具,其核心功能之一就是依赖管理。通过在项目根目录的 pom.xml 文件中声明所需依赖,Maven 会自动从中央仓库下载并管理这些库及其传递性依赖。
-
资源保护:我们采用了 try-with-resources 语句,这是 Java 7 引入的最佳实践。任何实现了 AutoCloseable 接口的资源(如 FileInputStream, Workbook)都可以在 try 声明中创建,无论代码块是正常执行还是抛出异常,Java 虚拟机都会保证自动调用其 close() 方法,从而彻底避免了资源泄露问题。
pom.xml文件核心配置
<dependencies>
<!-- Apache POI - 用于处理 .xls 格式 (Excel 97-2003) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.5</version>
</dependency>
<!-- Apache POI - 用于处理 .xlsx 格式 (Excel 2007 及更高版本) -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
</dependency>
2.3.2 代码封装为DAO模式
难点:
-
思维转变:从面向过程的编程思维转变为面向接口和抽象的编程思维。
-
职责分离:需要准确地将原来
StudentManagementSystem
和Main
中的数据操作代码剥离出来,放入不同的DAO实现类中。 -
依赖倒置:让上层(
Main.java
)依赖抽象(StudentDao
),而不是依赖具体实现。
解决方案:
-
定义接口先行:首先设计
StudentDao
接口,明确需要哪些数据操作方法。 -
逐步迁移:将原
StudentManagementSystem
中操作List<Student>
的代码,逐一迁移到StudentDaoListImpl
中,并实现接口方法。 -
创建新实现:基于
StudentDao
接口,分别创建StudentDaoFileImpl
和StudentDaoExcelImpl
,实现各自的文件读写逻辑。 -
修改调用方:最后,修改
Main.java
,让它通过接口来调用DAO,而不是直接调用具体的管理类。
三、测试案例
3.1 案例设计及预想
测试用例ID | 测试场景 | 操作步骤 | 预期结果 |
---|---|---|---|
TC-01 | Excel数据加载 | 1. 准备一个包含3条学生数据的students.xlsx 文件。2. 启动程序,选择Excel实现。 3. 选择“查询所有学生”。 |
程序能正确读取Excel文件,并显示文件中的3条学生信息。 |
TC-02 | 添加学生并持久化 | 1. 在TC-01的基础上,选择“添加学生”。 2. 输入一个新学生的信息。 3. 退出程序。 4. 重新打开程序并查询所有学生。 |
新添加的学生信息被保存到Excel文件中,程序重启后能查询到总共4条记录。 |
TC-03 | 删除学生 | 1. 在TC-02的基础上,选择“删除学生”。 2. 输入已存在的学生ID。 3. 查询所有学生。 |
指定ID的学生被成功删除,查询结果只显示剩余的3条记录。 |
TC-04 | 切换存储实现 | 1. 将代码中的DAO实现从 StudentDaoExcelImpl 改为 StudentDaoFileImpl 。2. 运行程序并添加一个学生。 3. 检查项目目录。 |
程序正常运行,并在项目根目录下生成一个 students.txt 文件,内容为新添加的学生数据。 |
3.2 问题分析
3.2.1 Excel 文件导入导出功能的实现
问题:程序首次运行或 students.xlsx
文件被意外删除时,loadStudentsFromFile
方法中的 new FileInputStream(FILENAME)
会抛出 FileNotFoundException
,导致程序崩溃。一个健壮的系统应该能优雅地处理这种情况,并自动创建必要的文件。
分析:查看 loadStudentsFromFile
方法的源码,其内部调用了 ExcelUtils.readStudentsFromExcel()
。虽然当前代码已经
通过 try-catch (IOException e)
捕获了 FileNotFoundException
,防止了程序崩溃,但其处理方式是返回一个空列表。这虽然安
全,但用户体验不佳,因为用户无法直观地知道文件不存在。此外,当 addStudent
或 saveStudentsToFile
被首次调用时,
FileOutputStream
会自动创建文件,但这是一种隐式行为,不够明确。
解决方案:
-
优化异常处理逻辑:在
loadStudentsFromFile
方法的catch
块中,增加更具体的判断。如果捕获到的是FileNotFoundException
,则打印一条明确的提示信息,如"Info: students.xlsx not found. A new file will be created on first save."
,然后再返回空列表。这样既保证了程序的健壮性,又提升了信息透明度。 -
确保文件自动创建:
saveStudentsToFile
方法中的new FileOutputStream(FILENAME)
已经具备在文件不存在时自动创建文
件的能力。因此,我们无需额外编写创建文件的逻辑,只需确保在首次保存操作(如添加第一个学生)时,该方法能被正确调用即可。现有的代
码结构已经支持这一点。
3.2.2 Excel 文件格式问题
问题:Excel文件中的数据格式不正确(如年龄列输入了文本),导致程序在读取时抛出 NumberFormatException
,中断整个数据加载过程。
分析:当前代码在读取数据时,直接调用 row.getCell().getStringCellValue()
或 (int) row.getCell().getNumericCellValue()
。如果单元格内容为空或格式不匹配(例如,年龄列是文本),这些方法会抛出 IllegalStateException
或 NumberFormatException
。由于整个读取过程在一个大的 try-catch
块中,任何一行的数据错误都会导致后续所有有效数据无法被加载。
解决方案:
-
封装安全的单元格读取:创建一个私有的辅助方法,例如
getCellValueSafely(Row row, int cellIndex, String defaultValue)
,用于安全地读取字符串。该方法内部会检查单元格是否为null
或单元格类型是否为CellType.BLANK
,如果是,则返回defaultValue
,否则调用getStringCellValue()
。 -
封装安全的数值读取:同样,创建一个
getNumericCellValueSafely(Row row, int cellIndex, double defaultValue)
方法。它内部会检查单元格是否为null
或非数值类型,并使用try-catch
捕获NumberFormatException
,在发生任何错误时返回defaultValue
。 -
将容错逻辑应用于每一行:在遍历每一行数据的循环内部,为每一行的数据解析单独使用一个
try-catch
块。这样,即使某一行数据格式错误导致异常,程序也只会打印该行的错误信息(如"Warning: Skipping row 2 due to invalid data format."
),然后继续处理下一行,从而保证了最大程度的数据加载能力。
四、实验使用环境
- 操作系统:Windows 11专业版
- 开发工具 (IDE):Eclipse IDE 2025.6
- Java 开发工具包 (JDK):JavaSE-1.8
- 构建工具:Apache Maven 3.8.6
- 第三方库:
- Apache POI 5.2.3 (用于操作Excel文件)
- Apache POI OOXML 5.2.3
五、项目总结
本次学生信息管理系统的重构项目,成功地将一个结构混乱、功能单一的原型程序,改造为一个架构清晰、低耦合、高扩展性的分层应用。通过引入DAO设计模式,我们深刻体会到了面向接口编程和分层架构在软件开发中的巨大优势。
主要收获:
-
掌握了DAO模式:不仅理解了其理论,更通过实践将其应用于解决实际问题,实现了业务逻辑与数据访问的解耦。
-
提升了工程化能力:学会了使用标准的包结构、依赖管理(Maven)和第三方库(Apache POI)来构建更专业的Java项目。
-
增强了问题解决能力:在处理Excel读写、异常处理等难点时,通过查阅资料和不断调试,最终找到了稳健的解决方案。
反思与展望:
虽然项目已达到预期目标,但仍存在可优化的空间。例如,可以引入工厂模式或依赖注入框架(如Spring)来管理DAO实例的创建,进一步降低表示层与具体实现的耦合。此外,还可以增加更复杂的数据校验、多条件查询等功能,使系统更加完善。
总而言之,本次重构是一次非常有价值的实践,它不仅提升了代码质量,更重要的是锻炼了我们的软件设计思维,为未来开发更复杂的系统奠定了坚实的基础。
六、附件-参考文件
- Maven简要使用说明:在IDEA中创建一个基于POI的处理Excel文件的简单Java Maven项目
https://www.cnblogs.com/zhrb/p/18612024