“吃货联盟订餐系统”分析及改进
(1)来源
本次分析的项目为“吃货联盟订餐系统”,源来源于博客园,博客地址为: https://www.cnblogs.com/benben2013A/p/7084538.html。
(2)运行环境+运行结果
运行环境:
1.JDK版本:Java 8
2.操作系统:Windows 11
3.开发工具:IDEA
运行结果:
(3)主要问题和改善
3.1. 代码结构与设计:所有逻辑全部堆砌在 main 方法中,可读性差,不利于维护和扩展。
改善:
- 创建 Order(订单)类,封装订单属性(订餐人、菜品、金额、状态等)。
- 创建 Dish(菜品)类,封装菜品属性(名称、单价、点赞数)。
- 创建 OrderManager(订单管理)类,负责处理订餐、签收、删除等核心业务逻辑。
- main 方法所在类仅负责用户界面交互和流程控制。
3.2.数据存储:使用多个固定大小的数组并行存储订单信息,数组长度固定为4,导致系统容量有限且容易出错(如数组下标管理混乱)。添加、删除订单时需要手动维护多个数组,非常繁琐且易遗漏。
改善:
使用 ArrayList
3.3功能逻辑缺陷:
1.送餐费计算:订餐成功后的总计金额包含了餐费和送餐费,但送餐费的计算逻辑只在订餐时显示,并未存入 sumPrices 数组。
2. 订单状态:states 数组默认值为0,但初始化了两个订单后,第二个订单 states[1] 未显式赋值,其状态为0(已预定),这可能不符合预期
3. 点赞功能:点赞后没有即时反馈当前菜品的总赞数,用户体验稍差。
改善:
- 修正订餐逻辑,将计算后的 sumPrice + deliCharge 存入 sumPrices 数组。
- 在添加新订单时,明确设置其状态为“已预定”。
- 点赞成功后,重新显示当前菜品的点赞总数。
3.4代码细节问题:
- 拼写错误:变量名 priaiseNum 拼写错误,应为 praiseNum;注释中存在错别字,如“序叼”、“胡订单序号”。
- 格式混乱:“查看餐袋”功能中,输出格式使用 \t 对齐,当内容长度不一时,界面会非常混乱,难以阅读。
改善: - 修正所有拼写错误和不规范的命名。
- 优化“查看餐袋”的输出格式,使用 String.format 或 System.out.printf 进行格式化输出,确保表格对齐美观。
(4)新代码
4.1Order类:


4.2Dish类:

4.3OrderManager类:


4.4主程序框架及改进点:
public class OrderingApp {
private static OrderManager manager = new OrderManager();
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
// ... 初始化一些菜品和订单数据 ...
int num;
do {
// 显示菜单...
int choose = getIntInput("请选择:"); // 封装了异常处理的输入方法
switch (choose) {
case 1:
placeOrder(); // 将订餐逻辑抽离成单独方法
break;
case 2:
viewOrders(); // 查看餐袋
break;
// ... 其他case ...
case 6:
System.out.println("感谢使用,再见!");
return;
default:
System.out.println("无效选择,请重新输入。");
}
num = getIntInput("输入0返回主菜单,其他数字退出系统:");
} while (num == 0);
}
// 改进点:封装了输入验证的整数读取方法
private static int getIntInput(String prompt) {
int input = -1;
while (true) {
System.out.print(prompt);
try {
input = scanner.nextInt();
break;
} catch (InputMismatchException e) {
System.out.println("输入错误,请输入一个整数。");
scanner.next(); // 清除错误输入
}
}
return input;
}
// 改进点:订餐逻辑,使用Order类和ArrayList
private static void placeOrder() {
System.out.println("***我要订餐***");
// 1. 获取用户输入(已做验证)
String name = getStringInput("请输入订餐人姓名:");
// ... 显示菜品、选择编号、份数等(已做边界检查)...
int dishChoice = getIntInputInRange("请选择菜品编号:", 1, dishNames.length);
// ... 计算金额、获取送餐时间地址等 ...
// 2. 创建Order对象并交由Manager处理
Order newOrder = new Order(name, dishInfo, time, address, total);
if (manager.addOrder(newOrder)) {
System.out.println("订餐成功!");
} else {
// 理论上ArrayList不会满,但可以保留业务限制
System.out.println("订餐失败,系统繁忙。");
}
}
}
(5)重构后的软件测试




(6)总结
6.1:难点:
- 理解原有代码逻辑:原代码将所有功能混杂在 main 方法的 switch 语句中,变量众多,循环和条件嵌套较深。理清每个变量的作用和程序执行流程是分析阶段的主要难点。
- 重构时的职责划分:如何将原本纠缠在一起的代码合理地拆分到不同的类和方法中,确保“高内聚、低耦合”,是设计阶段需要仔细思考的问题。
- 确保重构后功能一致:在修改代码结构和修复缺陷的同时,必须保证所有原有功能都能正常工作,需要设计较全面的测试用例进行回归测试。
6.2耗时较久的环节:
- 输入验证的完善:设计一个既能捕获异常、又能进行范围检查,且方便复用的输入工具方法花费了较多时间。
- 界面格式化输出:为了让“查看餐袋”功能输出整洁的表格,调整 String.format 的格式符和对齐方式比预想的要繁琐一些。
6.3对逆向软件工程的一些思考:
- 逆向工程不仅是读代码,更是理解设计意图:即使是这样一个简单的项目,也能从代码的写法(如用并行数组)看出作者当时对数据管理的朴素理解。逆向的过程,就是与原作者“对话”,理解其解决问题的原始思路。
- “烂代码”是最好的教材:分析一个有缺陷的项目,比直接阅读一个完美项目收获更大。它能让你直观地感受到坏味道(如过长方法、重复代码)带来的维护成本,从而更深刻地理解设计原则和模式的价值。
- 重构是循序渐进的过程:不必试图一次性解决所有问题。可以从最简单的代码格式化、变量重命名开始,逐步深入到结构重组和逻辑优化。每一步改进都让代码变得更好,这个过程本身就很有意义。
- 测试是重构的安全网:在没有自动化测试的情况下进行手动重构是有风险的。这让我意识到,为现有代码编写测试(即使是手动的测试用例列表)是多么重要,它能让你有信心去修改代码而不怕破坏既有功能。
浙公网安备 33010602011771号