今天,我们将通过一个学生信息管理系统的实战案例,把这些知识串联起来,体验从需求分析到代码实现,再到优化重构的完整过程。
一、需求分析:我们要做什么?
学生信息管理系统的核心需求是实现对学生信息的增删改查(CRUD) ,具体包括:
- 添加:录入学生的学号、姓名、年龄等信息
- 查询:查看单个学生信息或所有学生列表
- 修改:更新学生的姓名、年龄等信息
- 删除:移除指定学生的信息
- 退出:结束程序运行
我们将用 Dart 控制台程序实现这些功能,重点练习类的设计、集合的操作和函数的封装。
二、核心技术点梳理
在开始编码前,先明确我们会用到的技术:
- 类(Class) :定义
Student类封装学生信息 - List 集合:用
List<Student>存储所有学生数据 - 函数封装:将增删改查各功能拆分为独立函数
- 用户交互:通过控制台输入输出与用户交互
- 流程控制:用循环和分支实现菜单逻辑
三、代码实现:从 0 到 1 搭建系统
1. 步骤 1:定义学生类(数据模型)
首先,我们需要一个 Student 类来规范学生信息的数据结构,包含学号(唯一标识)、姓名、年龄三个核心属性:
class Student {
final String id; // 学号(不可变,唯一标识)
String name; // 姓名
int age; // 年龄
// 构造函数
Student({required this.id, required this.name, required this.age});
// 重写toString方法,方便打印学生信息
@override
String toString() {
return "学号:$id,姓名:$name,年龄:$age";
}
}
id设为final确保学号不可修改,避免重复或误改- 重写
toString()方便后续打印学生信息时显示清晰格式
2. 步骤 2:初始化数据和工具函数
接下来,我们需要一个存储学生的列表,以及一些工具函数(如判断学号是否存在):
// 存储所有学生的列表
List<Student> students = [];
// 检查学号是否已存在(用于避免重复添加)
bool isIdExists(String id) {
return students.any((student) => student.id == id);
}
3. 步骤 3:实现增删改查核心功能
将每个操作封装为独立函数,保证单一职责:
(1)添加学生
void addStudent() {
print("\n===== 添加学生 =====");
// 输入学号(确保唯一)
String id;
while (true) {
print("请输入学号:");
id = stdin.readLineSync()?.trim() ?? "";
if (id.isEmpty) {
print("学号不能为空!");
} else if (isIdExists(id)) {
print("该学号已存在,请重新输入!");
} else {
break; // 学号合法,退出循环
}
}
// 输入姓名
String name;
while (true) {
print("请输入姓名:");
name = stdin.readLineSync()?.trim() ?? "";
if (name.isEmpty) {
print("姓名不能为空!");
} else {
break;
}
}
// 输入年龄(确保是数字)
int age;
while (true) {
print("请输入年龄:");
String? ageInput = stdin.readLineSync()?.trim();
age = int.tryParse(ageInput ?? "") ?? -1;
if (age <= 0) {
print("请输入有效的年龄!");
} else {
break;
}
}
// 添加到列表
students.add(Student(id: id, name: name, age: age));
print("添加成功!学生信息:$students.last");
}
- 通过循环验证输入合法性(非空、学号唯一、年龄为正数)
- 输入验证能有效避免脏数据
(2)查询学生
实现两种查询方式:查询所有和按学号查询:
void queryAllStudents() {
print("\n===== 所有学生 =====");
if (students.isEmpty) {
print("暂无学生信息!");
return;
}
for (int i = 0; i < students.length; i++) {
print("${i + 1}. ${students[i]}"); // 带序号打印
}
}
void queryStudentById() {
print("\n===== 按学号查询 =====");
print("请输入要查询的学号:");
String id = stdin.readLineSync()?.trim() ?? "";
// 查找学生(where返回满足条件的可迭代对象)
Student? student = students.where((s) => s.id == id).firstOrNull;
if (student != null) {
print("查询结果:$student");
} else {
print("未找到学号为 $id 的学生!");
}
}
- 用
where()结合firstOrNull快速查找学生 - 空列表判断提升用户体验
(3)修改学生信息
void updateStudent() {
print("\n===== 修改学生 =====");
print("请输入要修改的学生学号:");
String id = stdin.readLineSync()?.trim() ?? "";
Student? student = students.where((s) => s.id == id).firstOrNull;
if (student == null) {
print("未找到学号为 $id 的学生!");
return;
}
print("当前信息:$student");
// 修改姓名(可选,回车跳过)
print("请输入新姓名(回车不修改):");
String? newName = stdin.readLineSync()?.trim();
if (newName != null && newName.isNotEmpty) {
student.name = newName;
}
// 修改年龄(可选,回车跳过)
print("请输入新年龄(回车不修改):");
String? ageInput = stdin.readLineSync()?.trim();
if (ageInput != null && ageInput.isNotEmpty) {
int newAge = int.tryParse(ageInput) ?? -1;
if (newAge > 0) {
student.age = newAge;
} else {
print("年龄输入无效,未修改!");
}
}
print("修改成功!更新后信息:$student");
}
- 支持部分修改(回车跳过),更灵活
- 对无效输入进行容错处理
(4)删除学生
void deleteStudent() {
print("\n===== 删除学生 =====");
print("请输入要删除的学生学号:");
String id = stdin.readLineSync()?.trim() ?? "";
Student? student = students.where((s) => s.id == id).firstOrNull;
if (student == null) {
print("未找到学号为 $id 的学生!");
return;
}
// 二次确认
print("确定要删除以下学生吗?[y/n]");
print(student);
String? confirm = stdin.readLineSync()?.trim()?.toLowerCase();
if (confirm == "y" || confirm == "yes") {
students.remove(student);
print("删除成功!");
} else {
print("已取消删除!");
}
}
- 二次确认避免误操作,提升安全性
- 用
remove()从列表中删除元素
4. 步骤 4:实现主菜单和程序入口
最后,我们需要一个主循环展示菜单,接收用户选择并调用对应功能:
import 'dart:io'; // 导入输入输出库
void main() {
print("===== 学生信息管理系统 =====");
// 主循环(程序入口)
while (true) {
print("\n请选择操作:");
print("1. 添加学生");
print("2. 查看所有学生");
print("3. 按学号查询");
print("4. 修改学生信息");
print("5. 删除学生");
print("0. 退出系统");
String? choice = stdin.readLineSync()?.trim();
switch (choice) {
case "1":
addStudent();
break;
case "2":
queryAllStudents();
break;
case "3":
queryStudentById();
break;
case "4":
updateStudent();
break;
case "5":
deleteStudent();
break;
case "0":
print("感谢使用,再见!");
return; // 退出程序
default:
print("无效选择,请输入0-5之间的数字!");
}
}
}
- 用
while (true)实现无限循环,直到用户选择退出 switch-case处理菜单选择,逻辑清晰
四、代码优化:从 “能跑” 到 “易读”
上面的代码已经能实现基本功能,但我们可以从以下几个方面优化,让代码更易维护:
1. 优化 1:提取常量和工具类
将重复使用的提示文字、菜单选项等提取为常量,避免硬编码:
// 常量类(存储固定文本)
class Constants {
static const String appTitle = "===== 学生信息管理系统 =====";
static const String menuTips = "\n请选择操作:";
static const List<String> menuOptions = [
"1. 添加学生",
"2. 查看所有学生",
"3. 按学号查询",
"4. 修改学生信息",
"5. 删除学生",
"0. 退出系统",
];
// ... 其他常量
}
// 工具类(封装输入输出逻辑)
class InputUtils {
// 安全读取输入(处理null和空字符串)
static String readLine({String tip = ""}) {
if (tip.isNotEmpty) print(tip);
return stdin.readLineSync()?.trim() ?? "";
}
// 读取整数(带验证)
static int? readInt({String tip = "", int min = 0}) {
String input = readLine(tip: tip);
int? value = int.tryParse(input);
return (value != null && value >= min) ? value : null;
}
}
2. 优化 2:拆分复杂函数
将 addStudent 中冗长的输入逻辑拆分为独立函数,比如:
// 从输入获取合法学号
String getValidId() {
while (true) {
String id = InputUtils.readLine(tip: "请输入学号:");
if (id.isEmpty) {
print("学号不能为空!");
} else if (isIdExists(id)) {
print("该学号已存在,请重新输入!");
} else {
return id;
}
}
}
// 调用时简化为:
String id = getValidId();
3. 优化 3:增加异常处理
用 try-catch 捕获可能的异常(如输入转换错误):
void updateStudent() {
try {
// 原有逻辑...
} catch (e) {
print("操作失败:${e.toString()}");
}
}
4. 优化 4:完善注释和文档
为类、函数添加注释,说明功能、参数和返回值:
/// 学生信息实体类
/// 存储学生的学号、姓名和年龄
class Student {
final String id; // 学号(唯一,不可修改)
String name; // 学生姓名
int age; // 学生年龄(正整数)
/// 构造函数
/// [id]:学号(必须唯一)
/// [name]:姓名(非空)
/// [age]:年龄(大于0)
Student({required this.id, required this.name, required this.age})
: assert(age > 0, "年龄必须大于0"); // 断言验证
}
五、扩展功能:让系统更实用
在基础功能上,我们还可以扩展一些实用功能:
- 数据持久化:用文件存储学生信息(下次启动不丢失)
- 按姓名查询:支持模糊搜索
- 排序功能:按学号或年龄排序
- 统计功能:计算平均年龄、学生总数等
以 “按姓名模糊查询” 为例:
void queryStudentByName() {
print("\n===== 按姓名查询 =====");
String keyword = InputUtils.readLine(tip: "请输入姓名关键字:");
if (keyword.isEmpty) {
print("关键字不能为空!");
return;
}
// 筛选姓名包含关键字的学生(忽略大小写)
List<Student> results = students
.where((s) => s.name.toLowerCase().contains(keyword.toLowerCase()))
.toList();
if (results.isEmpty) {
print("未找到包含 '$keyword' 的学生!");
} else {
print("查询到 ${results.length} 条结果:");
results.forEach((s) => print(s));
}
}