基于 ArrayList 与面向对象的外卖商家菜品管理系统实现
·
案例:外卖系统案例

(一)、核心需求拆解
本次开发的核心目标是:在原有 “外卖菜品上架、浏览” 功能的基础上,新增 “通过菜品唯一 ID 精准查询菜品信息” 的功能,同时保证系统逻辑完整、用户交互友好。拆解为 3 个具体子需求:
- 数据层:为菜品实体新增 “唯一 ID” 属性,作为查询标识;
- 业务层:实现 “根据输入 ID 遍历菜品列表、匹配并返回结果” 的逻辑;
- 交互层:新增查询入口,完善用户输入、结果反馈(找到 / 未找到)的交互流程。
(二)、分阶段解题思路
阶段 1:数据模型改造(Food 类)
核心思路:扩展实体属性,保证数据唯一性
- 新增核心字段:在原有
name/originalPrice等属性基础上,新增private int id字段,作为菜品的唯一标识(类似外卖平台的菜品编号); - 完善构造方法:
- 保留无参构造,满足基础对象创建需求;
- 扩展带参构造,加入
id参数,支持创建时直接赋值;
- 补充访问方法:为
id字段生成getId()/setId()方法,保证属性封装性(符合面向对象 “封装” 原则); - 优化展示逻辑:重写
toString()方法,将id融入菜品信息展示,方便用户查看 ID。
关键考量:
- ID 必须唯一,因此不在用户输入环节手动填写,而是由系统自动分配(避免重复)。
阶段 2:业务逻辑扩展(FoodManager 类)
子步骤 1:设计 ID 自动生成机制
- 新增自增变量:在
FoodManager中定义private int nextId = 1,初始值为 1,每次上架菜品后自增; - 关联上架功能:修改
addFood()方法,创建Food对象时,将nextId作为 ID 传入,随后nextId++,保证每个新菜品的 ID 唯一且连续(如第 1 个菜品 ID=1,第 2 个 = 2,以此类推)。
子步骤 2:实现 “ID 查询” 核心逻辑
- 入口设计:在主菜单中新增 “3. 根据 ID 查询菜品” 选项,关联
queryFoodById()方法; - 前置校验:查询前先判断菜品列表是否为空,若为空直接提示 “暂无菜品”,避免无效遍历;
- 用户输入处理:接收用户输入的目标 ID(
int targetId),并处理输入后的换行符(避免后续输入异常); - 遍历匹配逻辑:
- 定义
Food targetFood = null作为 “匹配结果容器”; - 增强 for 循环遍历
foodList,逐个对比菜品id与用户输入的targetId; - 找到匹配项时,将菜品对象赋值给
targetFood并终止循环(提升效率);
- 定义
- 结果反馈:
- 若
targetFood != null:打印匹配到的菜品详细信息; - 若
targetFood == null:提示 “未找到对应 ID 的菜品”,保证用户体验。
- 若
子步骤 3:交互流程完善
- 菜单扩展:在
showMenu()的 switch-case 中新增case 3,调用查询方法; - 异常防护:对用户输入的非数字 ID(如字母、符号),虽基础版本未做校验,但预留了扩展空间(进阶可加输入合法性判断)。
阶段 3:程序入口保持稳定(FoodTest 类)
核心思路:开闭原则(对扩展开放、对修改关闭)
- 无需修改
FoodTest类的任何代码,仅需创建FoodManager对象并调用showMenu(); - 原有 “上架、浏览” 功能不受影响,新增功能通过扩展实现,保证系统稳定性。
(三)、关键设计原则与考量
1. 面向对象核心原则
- 封装性:菜品的
id属性私有化,仅通过 Getter/Setter 访问,避免外部直接修改; - 开闭原则:新增查询功能时,仅扩展代码(新增方法 / 字段),不修改原有 “上架、浏览” 的核心逻辑;
- 单一职责:
Food类仅负责封装菜品数据,FoodManager仅负责业务逻辑,FoodTest仅负责启动程序,职责清晰。
2. 业务逻辑合理性
- ID 唯一性保障:系统自动分配 ID,而非用户输入,从源头避免 ID 重复;
- 遍历效率:找到匹配 ID 后立即
break终止循环,无需遍历全部列表,提升查询效率; - 用户体验:
- 查询前校验列表是否为空,避免无效操作;
- 明确的结果反馈(找到 / 未找到),而非无提示或报错。
3. 扩展性预留
- 查询方法独立封装为
queryFoodById(),后续可基于此扩展 “ID 修改、ID 删除” 等功能; - 自增 ID 机制可灵活调整(如从 1000 开始编号,适配实际业务场景)。
(四)、核心思路总结
- 数据先行:先扩展实体属性(新增 ID),为查询功能提供数据基础;
- 逻辑闭环:查询功能包含 “输入→校验→遍历匹配→结果反馈” 完整流程,无逻辑断点;
- 兼容扩展:在不破坏原有功能的前提下扩展新功能,符合软件开发的核心原则,同时预留后续扩展空间(如 ID 删除、修改)。
二、完整代码示例
(一)、菜品类(Food.java)
封装菜品的核心信息,新增id字段用于唯一标识。
public class Food {
// 菜品唯一ID
private int id;
// 菜品名称
private String name;
// 菜品原价
private double originalPrice;
// 菜品优惠价
private double discountPrice;
// 菜品其他信息(如口味、规格等)
private String info;
// 无参构造
public Food() {}
// 带参构造(包含id)
public Food(int id, String name, double originalPrice, double discountPrice, String info) {
this.id = id;
this.name = name;
this.originalPrice = originalPrice;
this.discountPrice = discountPrice;
this.info = info;
}
// Getter和Setter方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getOriginalPrice() {
return originalPrice;
}
public void setOriginalPrice(double originalPrice) {
this.originalPrice = originalPrice;
}
public double getDiscountPrice() {
return discountPrice;
}
public void setDiscountPrice(double discountPrice) {
this.discountPrice = discountPrice;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
// 重写toString方法,方便打印菜品信息
@Override
public String toString() {
return "【ID:" + id + "】" + name + "\n" +
"信息:" + info + "\n" +
"原价:¥" + originalPrice + " 优惠价:¥" + discountPrice + "\n" +
"------------------------";
}
}
(二)、菜品管理类(FoodManager.java)
在原有功能基础上,新增根据 ID 查询菜品的功能。
import java.util.ArrayList;
import java.util.Scanner;
public class FoodManager {
// 存储所有上架的菜品
private ArrayList<Food> foodList = new ArrayList<>();
// 扫描器,用于接收用户输入
private Scanner sc = new Scanner(System.in);
// 用于生成菜品ID(自增)
private int nextId = 1;
// 展示操作界面
public void showMenu() {
while (true) {
System.out.println("\n===== 外卖商家系统 =====");
System.out.println("1. 上架菜品");
System.out.println("2. 浏览所有菜品");
System.out.println("3. 根据ID查询菜品");
System.out.println("4. 退出系统");
System.out.print("请输入您的选择:");
int choice = sc.nextInt();
sc.nextLine(); // 处理换行符
switch (choice) {
case 1:
addFood();
break;
case 2:
showAllFoods();
break;
case 3:
queryFoodById();
break;
case 4:
System.out.println("退出系统,感谢使用!");
return;
default:
System.out.println("输入有误,请重新选择!");
}
}
}
// 上架菜品功能(自动分配ID)
private void addFood() {
System.out.println("\n===== 上架新菜品 =====");
System.out.print("请输入菜品名称:");
String name = sc.nextLine();
System.out.print("请输入菜品原价:");
double originalPrice = sc.nextDouble();
System.out.print("请输入菜品优惠价:");
double discountPrice = sc.nextDouble();
sc.nextLine(); // 处理换行符
System.out.print("请输入菜品其他信息(如口味、规格):");
String info = sc.nextLine();
// 创建菜品对象(自动生成ID)并加入集合
Food food = new Food(nextId++, name, originalPrice, discountPrice, info);
foodList.add(food);
System.out.println("✅ 菜品【" + name + "】上架成功!菜品ID:" + food.getId());
}
// 浏览所有菜品功能
private void showAllFoods() {
System.out.println("\n===== 所有上架菜品 =====");
if (foodList.isEmpty()) {
System.out.println("⚠️ 暂无上架菜品,请先上架菜品!");
return;
}
// 遍历集合,打印所有菜品信息
for (Food food : foodList) {
System.out.println(food);
}
}
// 根据ID查询菜品功能(新增)
private void queryFoodById() {
System.out.println("\n===== 根据ID查询菜品 =====");
if (foodList.isEmpty()) {
System.out.println("⚠️ 暂无上架菜品,请先上架菜品!");
return;
}
System.out.print("请输入要查询的菜品ID:");
int targetId = sc.nextInt();
sc.nextLine(); // 处理换行符
// 遍历集合查找匹配ID的菜品
Food targetFood = null;
for (Food food : foodList) {
if (food.getId() == targetId) {
targetFood = food;
break;
}
}
// 输出查询结果
if (targetFood != null) {
System.out.println("✅ 查询到菜品信息:");
System.out.println(targetFood);
} else {
System.out.println("❌ 未找到ID为【" + targetId + "】的菜品!");
}
}
}
(三)、测试类(FoodTest.java)
程序入口,启动商家管理系统。
public class FoodTest {
public static void main(String[] args) {
// 创建管理类对象并启动界面
FoodManager manager = new FoodManager();
manager.showMenu();
}
}
三、核心知识点总结
(一)、面向对象编程核心
1. 封装特性强化
- 属性封装:为菜品新增
id私有字段,通过getId()/setId()方法控制访问,避免外部直接修改,保证数据安全性; - 方法封装:将 “ID 查询” 逻辑独立封装为
queryFoodById()方法,与 “上架”“浏览” 方法分离,符合 “单一职责原则”,便于维护和复用。
2. 类的扩展与开闭原则
- 扩展不修改:新增 ID 查询功能时,仅在原有
Food类新增字段 / 方法、FoodManager类新增查询方法,未改动 “上架”“浏览” 核心逻辑,遵循 “对扩展开放、对修改关闭” 的开闭原则; - 构造方法设计:保留无参构造、扩展带参构造,兼顾不同场景下的对象创建需求(空对象初始化 / 全属性初始化)。
(二)、ArrayList 集合核心应用
1. 集合遍历与匹配
- 增强 for 循环遍历:通过
for (Food food : foodList)遍历菜品列表,逐个对比food.getId()与目标 ID,实现精准匹配; - 匹配结果处理:定义
targetFood变量接收匹配结果,遍历中找到目标后立即break终止循环,提升查询效率。
2. 集合数据管理
- 动态存储:利用
ArrayList的动态扩容特性,存储数量不固定的菜品对象,无需担心长度限制; - 数据校验:查询前通过
foodList.isEmpty()判断集合是否为空,避免空列表遍历导致的无效操作。
(三)、业务逻辑设计
1. 唯一标识(ID)设计
- 自增 ID 机制:通过
nextId自增变量为菜品自动分配唯一 ID,避免用户手动输入导致的重复问题,符合实际业务中 “菜品编号唯一” 的需求; - ID 关联展示:重写
toString()方法,将 ID 融入菜品信息展示,为查询功能提供可视化标识。
2. 交互逻辑设计
- 用户输入处理:使用
Scanner接收用户输入的 ID,处理换行符避免输入异常; - 结果反馈:查询结果分为 “找到菜品(展示详情)” 和 “未找到菜品(提示信息)” 两种情况,保证交互的友好性和完整性。
(四)、Java 基础语法
1. 流程控制
- 分支结构:在主菜单中通过
switch-case新增查询功能入口,扩展程序功能; - 循环结构:增强 for 循环实现列表遍历,
while(true)实现菜单循环展示。
2. 变量与数据类型
- 整型变量应用:
id和nextId使用int类型,适配菜品编号的数值特性; - 引用类型赋值:通过
Food targetFood = null初始化引用变量,接收匹配到的菜品对象,利用 “引用指向对象” 的特性实现结果存储。
(五)、核心知识点速记
- 封装是面向对象的核心,私有属性需通过 Getter/Setter 访问,业务逻辑需封装为独立方法;
- ArrayList 遍历匹配是查询功能的核心,增强 for 循环简洁高效,匹配后及时终止循环可提升性能;
- 唯一 ID 设计需保证唯一性,系统自动分配(自增)是实际业务中最常用的实现方式;
- 交互逻辑需兼顾 “输入处理、前置校验、结果反馈”,保证程序的实用性和友好性。
更多推荐




所有评论(0)